mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-29 08:41:20 +00:00
Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d285efe3b5 | ||
|
|
a64c8d3205 | ||
|
|
82c52c3fab | ||
|
|
dd1e90fcd7 | ||
|
|
ef0155636a | ||
|
|
9d550fbffa | ||
|
|
fcef691e0a | ||
|
|
839c172612 | ||
|
|
9f49ac8249 | ||
|
|
fde550ef66 | ||
|
|
2b3993a595 | ||
|
|
de3e86791c | ||
|
|
6f66f77b09 | ||
|
|
d853aba955 | ||
|
|
cd62693792 | ||
|
|
cb4c8f3878 | ||
|
|
4d371d5f5f | ||
|
|
72963b01e1 | ||
|
|
80a18e82b7 | ||
|
|
574e687734 | ||
|
|
c725ec50ff | ||
|
|
c6f6e306e0 | ||
|
|
b847135321 |
No files matched your search
@@ -567,6 +567,8 @@ if(BUILD_TESTING)
|
||||
|
||||
add_test(NAME texture_cache_layered_image
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --layered-image-only)
|
||||
add_test(NAME host_image_allocation
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --host-image-allocation-only)
|
||||
add_test(NAME texture_cache_image_views
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --image-view-cache-only)
|
||||
add_test(NAME texture_cache_storage_sampled
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
namespace Libs::Graphics {
|
||||
|
||||
struct VulkanImage;
|
||||
struct VulkanMemory;
|
||||
|
||||
inline constexpr uint32_t VULKAN_TARGET_API_VERSION = VK_API_VERSION_1_3;
|
||||
|
||||
@@ -32,6 +31,7 @@ struct GraphicContext {
|
||||
bool compute_subgroup_size_control_enabled = false;
|
||||
bool sample_rate_shading_enabled = false;
|
||||
bool attachment_feedback_loop_enabled = false;
|
||||
bool provoking_vertex_last_enabled = false;
|
||||
bool supports_block_texel_view = false;
|
||||
bool mesh_shader_enabled = false;
|
||||
vk::PhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = {};
|
||||
@@ -117,12 +117,6 @@ private:
|
||||
m_image_format_properties;
|
||||
};
|
||||
|
||||
struct VulkanMemory {
|
||||
vk::MemoryPropertyFlags property = {};
|
||||
vk::MemoryPropertyFlags preferred_property = {};
|
||||
VmaAllocation allocation = nullptr;
|
||||
};
|
||||
|
||||
struct VulkanImageState {
|
||||
vk::PipelineStageFlags2 pl_stage = vk::PipelineStageFlagBits2::eAllCommands;
|
||||
vk::AccessFlags2 access_mask = vk::AccessFlagBits2::eNone;
|
||||
@@ -144,7 +138,7 @@ struct VulkanImage {
|
||||
vk::Image image = nullptr;
|
||||
VulkanImageState state;
|
||||
std::vector<VulkanImageState> subresource_states;
|
||||
Graphics::VulkanMemory memory;
|
||||
VmaAllocation allocation = nullptr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -277,10 +277,10 @@ void BufferCache::ReadMemoryOnGpu(uint64_t vaddr, uint64_t size, bool is_write)
|
||||
"memory invalidation");
|
||||
},
|
||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
||||
for (const auto range: m_gpu_modified_ranges.Intersections(address, bytes)) {
|
||||
m_gpu_modified_ranges.ForEachIntersection(address, bytes, [&](RangeSet::Range range) {
|
||||
copies.push_back(
|
||||
{&buffer, buffer.Offset(range.address), range.address, range.size});
|
||||
}
|
||||
});
|
||||
});
|
||||
if (!copies.empty()) {
|
||||
DownloadBufferMemory(copies);
|
||||
|
||||
+3
-7
@@ -187,15 +187,11 @@ TextureCache::TextureCache(GraphicContext& graphics, CommandScheduler& scheduler
|
||||
}
|
||||
|
||||
TextureCache::~TextureCache() {
|
||||
std::vector<ImageId> registered;
|
||||
m_slot_images.ForEach([&](ImageId id, const Image& image) {
|
||||
if (image.registered) {
|
||||
registered.push_back(id);
|
||||
UnregisterImage(id);
|
||||
}
|
||||
});
|
||||
for (const auto id: registered) {
|
||||
UnregisterImage(id);
|
||||
}
|
||||
}
|
||||
|
||||
bool TextureCache::SameBacking(const ImageInfo& cached, const ImageInfo& requested,
|
||||
@@ -1334,8 +1330,8 @@ ImageId TextureCache::FindImageFromRange(uint64_t address, uint64_t size, bool e
|
||||
if (!GuestRange {address, size}.Valid()) {
|
||||
return {};
|
||||
}
|
||||
std::scoped_lock lock {m_lock};
|
||||
std::vector<ImageId> matches;
|
||||
std::scoped_lock lock {m_lock};
|
||||
ImageIds matches;
|
||||
for (const auto id: FindImagesInRegion(address, size, false)) {
|
||||
auto owner = m_slot_images.try_get(id);
|
||||
if (owner == nullptr || owner->info.data.address != address) {
|
||||
|
||||
@@ -41,16 +41,13 @@ static bool DccAlphaOnMsb(const HW::ColorInfo& info) {
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer& buffer,
|
||||
RenderColorInfo& r,
|
||||
void RenderExecutor::ResolveRenderColorTarget(CommandBuffer& buffer, RenderColorInfo& r,
|
||||
uint32_t render_target_slice_offset,
|
||||
uint32_t render_target_slot, bool ignore_target_mask,
|
||||
uint32_t rt_slot, bool ignore_target_mask,
|
||||
bool exact_format) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
|
||||
const auto rt_slot = (render_target_slot == UINT32_MAX ? render_target_first_bound_slot(buffer)
|
||||
: render_target_slot);
|
||||
const auto& rt = hw.GetRenderTarget(rt_slot);
|
||||
auto mask = render_target_mask_slot(hw.GetRenderTargetMask(), rt_slot);
|
||||
if (ignore_target_mask && rt.base.addr != 0 && mask == 0) {
|
||||
|
||||
@@ -208,13 +208,7 @@ void CommandScheduler::Wait(uint64_t tick) {
|
||||
}
|
||||
|
||||
void CommandScheduler::PopPendingOperations() {
|
||||
PopPendingOperations(true);
|
||||
}
|
||||
|
||||
void CommandScheduler::PopPendingOperations(bool refresh_gpu_tick) {
|
||||
if (refresh_gpu_tick) {
|
||||
m_master.Refresh();
|
||||
}
|
||||
m_master.Refresh();
|
||||
for (;;) {
|
||||
PendingOperation operation;
|
||||
{
|
||||
|
||||
@@ -81,7 +81,6 @@ private:
|
||||
};
|
||||
|
||||
void BeginNext();
|
||||
void PopPendingOperations(bool refresh_gpu_tick);
|
||||
void PriorityOperationsThread(std::stop_token stop);
|
||||
void RunOperation(Common::UniqueFunction<void>&& operation);
|
||||
|
||||
|
||||
@@ -478,7 +478,6 @@ static void McCheck(const HW::ModeControl& c) {
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(c.provoking_vtx_last != false);
|
||||
EXIT_NOT_IMPLEMENTED(c.persp_corr_dis != false);
|
||||
}
|
||||
|
||||
|
||||
@@ -97,10 +97,8 @@ static bool UsesStencilOpValue(uint8_t fail, uint8_t pass, uint8_t depth_fail) {
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer& buffer,
|
||||
RenderDepthInfo& r) {
|
||||
void RenderExecutor::ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepthInfo& r) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
(void)submit_id;
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto& z = hw.GetDepthRenderTarget();
|
||||
const auto& rc = hw.GetRenderControl();
|
||||
@@ -235,7 +233,6 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
DepthFatal("layered depth backing range is invalid");
|
||||
}
|
||||
r.depth_clear_enable = rc.depth_clear_enable;
|
||||
r.depth_meta_clear_enable = false;
|
||||
r.depth_load_clear_enable = r.depth_clear_enable;
|
||||
r.depth_clear_value = hw.GetDepthClearValue();
|
||||
r.depth_test_enable = dc.z_enable;
|
||||
|
||||
@@ -23,7 +23,6 @@ struct RenderDepthInfo {
|
||||
TextureCache::ImageDesc desc;
|
||||
bool depth_clear_enable = false;
|
||||
bool depth_load_clear_enable = false;
|
||||
bool depth_meta_clear_enable = false;
|
||||
float depth_clear_value = 0.0f;
|
||||
bool depth_test_enable = false;
|
||||
// Effective draw writes; discovery applies test, target-write and clear controls.
|
||||
|
||||
@@ -661,26 +661,17 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
||||
return;
|
||||
}
|
||||
|
||||
backing.format = info.pixel_format;
|
||||
backing.image_type = HostImageType(info.type);
|
||||
backing.extent = info.extent;
|
||||
backing.layers = info.IsVolume() ? 1u : info.resources.layers;
|
||||
backing.mip_levels = info.resources.levels;
|
||||
backing.samples = info.samples;
|
||||
backing.flags = ImageCreateFlags(graphics, info);
|
||||
backing.usage = ImageUsageFlags(graphics, info);
|
||||
|
||||
vk::ImageCreateInfo create {};
|
||||
create.flags = backing.flags;
|
||||
create.imageType = backing.image_type;
|
||||
create.extent = backing.extent;
|
||||
create.mipLevels = backing.mip_levels;
|
||||
create.arrayLayers = backing.layers;
|
||||
create.format = backing.format;
|
||||
create.flags = ImageCreateFlags(graphics, info);
|
||||
create.imageType = HostImageType(info.type);
|
||||
create.extent = info.extent;
|
||||
create.mipLevels = info.resources.levels;
|
||||
create.arrayLayers = info.IsVolume() ? 1u : info.resources.layers;
|
||||
create.format = info.pixel_format;
|
||||
create.tiling = vk::ImageTiling::eOptimal;
|
||||
create.initialLayout = backing.state.layout;
|
||||
create.usage = backing.usage;
|
||||
create.samples = vulkan_sample_count(backing.samples);
|
||||
create.initialLayout = vk::ImageLayout::eUndefined;
|
||||
create.usage = ImageUsageFlags(graphics, info);
|
||||
create.samples = vulkan_sample_count(info.samples);
|
||||
|
||||
vk::ImageFormatProperties properties {};
|
||||
if (graphics.GetImageFormatProperties(create.format, create.imageType, create.tiling,
|
||||
@@ -691,10 +682,9 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
||||
"flags=0x%x samples=%u\n",
|
||||
static_cast<int>(create.format), static_cast<int>(create.imageType),
|
||||
static_cast<vk::ImageUsageFlags::MaskType>(create.usage),
|
||||
static_cast<vk::ImageCreateFlags::MaskType>(create.flags), backing.samples);
|
||||
static_cast<vk::ImageCreateFlags::MaskType>(create.flags), info.samples);
|
||||
}
|
||||
|
||||
backing.memory.property = vk::MemoryPropertyFlagBits::eDeviceLocal;
|
||||
if (!graphics.CreateImage(create, backing)) {
|
||||
EXIT("failed to create image: extent=%ux%ux%u format=%d layers=%u levels=%u\n",
|
||||
create.extent.width, create.extent.height, create.extent.depth,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "graphics/guest_gpu/gpu_format.h"
|
||||
#include "graphics/host_gpu/renderer/image/tiler.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
@@ -104,18 +105,14 @@ vk::ComponentSwizzle TextureGetComponentSwizzle(uint8_t s) {
|
||||
return vk::ComponentSwizzle::eIdentity;
|
||||
}
|
||||
|
||||
static uint32_t TextureGetDstSel(uint32_t swizzle, uint32_t channel) {
|
||||
return (swizzle >> (channel * 3u)) & 0x7u;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle) {
|
||||
vk::ComponentMapping components {};
|
||||
components.r = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 0)));
|
||||
components.g = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 1)));
|
||||
components.b = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 2)));
|
||||
components.a = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 3)));
|
||||
components.r = TextureGetComponentSwizzle(GetDstSel(swizzle, 0));
|
||||
components.g = TextureGetComponentSwizzle(GetDstSel(swizzle, 1));
|
||||
components.b = TextureGetComponentSwizzle(GetDstSel(swizzle, 2));
|
||||
components.a = TextureGetComponentSwizzle(GetDstSel(swizzle, 3));
|
||||
return components;
|
||||
}
|
||||
|
||||
|
||||
@@ -404,30 +404,22 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
|
||||
}
|
||||
|
||||
struct NullImageSpec {
|
||||
vk::Format format;
|
||||
Prospero::BufferFormat guest_format;
|
||||
};
|
||||
|
||||
static NullImageSpec NullTextureSpec(const ShaderRecompiler::IR::ImageResource& resource) {
|
||||
switch (resource.numeric_class) {
|
||||
case Prospero::TextureNumericClass::Float:
|
||||
return {vk::Format::eR32Sfloat, Prospero::BufferFormat::k32Float};
|
||||
case Prospero::TextureNumericClass::Uint:
|
||||
return {vk::Format::eR32Uint, Prospero::BufferFormat::k32UInt};
|
||||
case Prospero::TextureNumericClass::Sint:
|
||||
return {vk::Format::eR32Sint, Prospero::BufferFormat::k32SInt};
|
||||
case Prospero::TextureNumericClass::Unsupported: break;
|
||||
}
|
||||
EXIT("null image has unsupported numeric class\n");
|
||||
}
|
||||
|
||||
static TextureCache::ImageDesc NullTextureDesc(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
TextureCache::BindingType binding) {
|
||||
const auto spec = NullTextureSpec(resource);
|
||||
TextureCache::ImageDesc desc {};
|
||||
desc.info.pixel_format = spec.format;
|
||||
desc.info.guest_format = spec.guest_format;
|
||||
switch (resource.numeric_class) {
|
||||
case Prospero::TextureNumericClass::Float:
|
||||
desc.info.guest_format = Prospero::BufferFormat::k32Float;
|
||||
break;
|
||||
case Prospero::TextureNumericClass::Uint:
|
||||
desc.info.guest_format = Prospero::BufferFormat::k32UInt;
|
||||
break;
|
||||
case Prospero::TextureNumericClass::Sint:
|
||||
desc.info.guest_format = Prospero::BufferFormat::k32SInt;
|
||||
break;
|
||||
default: EXIT("null image has unsupported numeric class\n");
|
||||
}
|
||||
desc.info.pixel_format = VulkanFormat(desc.info.guest_format);
|
||||
desc.info.type = Prospero::ImageType::kColor2D;
|
||||
desc.info.extent = {1, 1, 1};
|
||||
desc.info.resources = {1, 1};
|
||||
@@ -769,8 +761,7 @@ PreparedBindings RenderExecutor::PrepareBindings(const ShaderStageRuntime& runti
|
||||
const auto& program = *runtime.program;
|
||||
const auto& snapshot = runtime.resources;
|
||||
PreparedBindings prepared;
|
||||
prepared.program = runtime.program;
|
||||
prepared.snapshot = &runtime.resources;
|
||||
prepared.runtime = &runtime;
|
||||
prepared.images.reserve(program.info.images.size());
|
||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||
auto binding = ResolveTexture(program.info.images[i], snapshot.images[i]);
|
||||
@@ -795,9 +786,9 @@ PreparedBindings RenderExecutor::PrepareBindings(const ShaderStageRuntime& runti
|
||||
|
||||
void RenderExecutor::FindBuffers(PreparedBindings& prepared) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
||||
const auto& program = *prepared.program;
|
||||
const auto& snapshot = *prepared.snapshot;
|
||||
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();
|
||||
@@ -820,9 +811,9 @@ void RenderExecutor::FindBuffers(PreparedBindings& prepared) {
|
||||
|
||||
void RenderExecutor::RebindBuffers(PreparedBindings& prepared) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
||||
const auto& program = *prepared.program;
|
||||
const auto& snapshot = *prepared.snapshot;
|
||||
EXIT_IF(prepared.runtime == nullptr || !*prepared.runtime);
|
||||
const auto& program = *prepared.runtime->program;
|
||||
const auto& snapshot = prepared.runtime->resources;
|
||||
const auto& layout = program.bindings;
|
||||
EXIT_IF(prepared.buffer_sources.size() != program.info.buffers.size());
|
||||
|
||||
@@ -855,9 +846,9 @@ void RenderExecutor::RebindBuffers(PreparedBindings& prepared) {
|
||||
|
||||
void RenderExecutor::RebindImages(PreparedBindings& prepared) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
||||
const auto& program = *prepared.program;
|
||||
const auto& snapshot = *prepared.snapshot;
|
||||
EXIT_IF(prepared.runtime == nullptr || !*prepared.runtime);
|
||||
const auto& program = *prepared.runtime->program;
|
||||
const auto& snapshot = prepared.runtime->resources;
|
||||
auto& images = prepared.images;
|
||||
EXIT_IF(images.size() != program.info.images.size());
|
||||
auto& texture_cache = m_context.GetTextureCache();
|
||||
@@ -915,8 +906,8 @@ RenderExecutor::PrepareGraphicsBindings(const ShaderStageRuntime& vertex,
|
||||
if (bindings.pixel) {
|
||||
FindBuffers(*bindings.pixel);
|
||||
}
|
||||
if (bindings.vertex.program->info.uses_dma ||
|
||||
(bindings.pixel && bindings.pixel->program->info.uses_dma)) {
|
||||
if (bindings.vertex.runtime->program->info.uses_dma ||
|
||||
(bindings.pixel && bindings.pixel->runtime->program->info.uses_dma)) {
|
||||
m_context.GetGpuResources().PrepareBda();
|
||||
}
|
||||
RebindBuffers(bindings.vertex);
|
||||
@@ -947,13 +938,13 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer,
|
||||
? vk::ShaderStageFlagBits::eFragment
|
||||
: vk::ShaderStageFlags {};
|
||||
for (const auto* prepared: prepared_bindings) {
|
||||
EXIT_IF(prepared == nullptr || prepared->program == nullptr ||
|
||||
prepared->snapshot == nullptr);
|
||||
write_count += prepared->program->bindings.descriptors.size();
|
||||
for (const auto& binding: prepared->program->bindings.descriptors) {
|
||||
EXIT_IF(prepared == nullptr || prepared->runtime == nullptr || !*prepared->runtime);
|
||||
const auto& program = *prepared->runtime->program;
|
||||
write_count += program.bindings.descriptors.size();
|
||||
for (const auto& binding: program.bindings.descriptors) {
|
||||
descriptor_count += NativeDescriptorCount(binding);
|
||||
}
|
||||
const auto shader_stage = NativeShaderStage(prepared->program->stage);
|
||||
const auto shader_stage = NativeShaderStage(program.stage);
|
||||
push_stages |= shader_stage;
|
||||
EXIT_IF((pipeline_bind_point == vk::PipelineBindPoint::eGraphics &&
|
||||
(shader_stage & GraphicsStages) == vk::ShaderStageFlags {}) ||
|
||||
@@ -968,7 +959,7 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer,
|
||||
m_descriptor_writes.reserve(write_count);
|
||||
|
||||
for (auto* prepared: prepared_bindings) {
|
||||
const auto& program = *prepared->program;
|
||||
const auto& program = *prepared->runtime->program;
|
||||
auto& descriptors = *prepared;
|
||||
const auto shader_stage = NativeShaderStage(program.stage);
|
||||
const auto shader_stages = ShaderPipelineStages(shader_stage);
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
namespace ShaderRecompiler::IR {
|
||||
struct ResourceSnapshot;
|
||||
}
|
||||
struct ShaderStageRuntime;
|
||||
|
||||
struct TextureBinding {
|
||||
ImageId image_id;
|
||||
@@ -35,8 +33,8 @@ struct PreparedBindings {
|
||||
BufferId id;
|
||||
};
|
||||
|
||||
const ShaderRecompiler::IR::CompiledShaderInfo* program = nullptr;
|
||||
const ShaderRecompiler::IR::ResourceSnapshot* snapshot = nullptr;
|
||||
// The draw owns the immutable compiled-program/runtime-snapshot association through commit.
|
||||
const ShaderStageRuntime* runtime = nullptr;
|
||||
// Keep the resolved guest range through cache preparation; only the host buffer ID may
|
||||
// become stale and need resolving again when bindings are rebound.
|
||||
std::vector<BufferSource> buffer_sources;
|
||||
|
||||
@@ -728,6 +728,7 @@ PipelineCache::Pipeline& PipelineCache::CreateGraphicsPipeline(
|
||||
static_params.cull_back = !rect_list && mc.cull_back;
|
||||
static_params.cull_front = !rect_list && mc.cull_front;
|
||||
static_params.face = mc.face;
|
||||
static_params.provoking_vtx_last = mc.provoking_vtx_last;
|
||||
static_params.polygon_mode =
|
||||
ResolvePolygonMode(mc, static_params.cull_front, static_params.cull_back);
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ struct PipelineStaticParameters {
|
||||
bool cull_front = false;
|
||||
bool cull_back = false;
|
||||
bool face = false;
|
||||
bool provoking_vtx_last = false;
|
||||
vk::PolygonMode polygon_mode = vk::PolygonMode::eFill;
|
||||
uint8_t color_srcblend[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
||||
uint8_t color_comb_fcn[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
||||
@@ -68,7 +69,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) == 165);
|
||||
static_assert(sizeof(PipelineStaticParameters) == 166);
|
||||
|
||||
struct PipelineRenderingState {
|
||||
std::array<vk::Format, RENDER_COLOR_ATTACHMENTS_MAX> color_formats {};
|
||||
|
||||
@@ -595,6 +595,15 @@ void CreatePipelineInternal(
|
||||
#if !defined(__APPLE__)
|
||||
rasterizer.pNext = &clip_ext;
|
||||
#endif
|
||||
vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex {};
|
||||
EXIT_NOT_IMPLEMENTED(static_params.provoking_vtx_last &&
|
||||
!graphics.provoking_vertex_last_enabled);
|
||||
if (graphics.provoking_vertex_last_enabled) {
|
||||
provoking_vertex.provokingVertexMode = static_params.provoking_vtx_last
|
||||
? vk::ProvokingVertexModeEXT::eLastVertex : vk::ProvokingVertexModeEXT::eFirstVertex;
|
||||
provoking_vertex.pNext = rasterizer.pNext;
|
||||
rasterizer.pNext = &provoking_vertex;
|
||||
}
|
||||
rasterizer.cullMode = cull_mode;
|
||||
rasterizer.frontFace = front_face;
|
||||
rasterizer.polygonMode = static_params.polygon_mode;
|
||||
|
||||
@@ -179,13 +179,11 @@ private:
|
||||
[[nodiscard]] GraphicsBindings PrepareGraphicsBindings(const ShaderStageRuntime& vertex,
|
||||
const ShaderStageRuntime& pixel,
|
||||
bool pixel_active);
|
||||
void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer& buffer,
|
||||
RenderColorInfo& target, uint32_t render_target_slice_offset = 0,
|
||||
uint32_t render_target_slot = UINT32_MAX,
|
||||
void ResolveRenderColorTarget(CommandBuffer& buffer, RenderColorInfo& target,
|
||||
uint32_t render_target_slice_offset, uint32_t render_target_slot,
|
||||
bool ignore_target_mask = false, bool exact_format = false);
|
||||
void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer& buffer,
|
||||
RenderDepthInfo& target);
|
||||
[[nodiscard]] bool PrepareDrawRenderState(uint64_t submit_id, CommandBuffer& buffer,
|
||||
void ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepthInfo& target);
|
||||
[[nodiscard]] bool PrepareDrawRenderState(CommandBuffer& buffer,
|
||||
const DrawCallInfo& draw,
|
||||
uint32_t render_target_slice_offset,
|
||||
bool log_setup_phases, DrawRenderState& state);
|
||||
@@ -197,7 +195,7 @@ private:
|
||||
[[nodiscard]] RenderState AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors,
|
||||
uint32_t color_count, RenderDepthInfo& depth,
|
||||
const std::optional<PreparedBindings>& pixel = std::nullopt);
|
||||
[[nodiscard]] bool ResolveColorTargets(uint64_t submit_id, CommandBuffer& buffer,
|
||||
[[nodiscard]] bool ResolveColorTargets(CommandBuffer& buffer,
|
||||
uint32_t render_target_slice_offset);
|
||||
void BindImage(ImageId id, bool storage);
|
||||
void BindRenderTarget(ImageId id);
|
||||
|
||||
@@ -544,11 +544,11 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
|
||||
!cache.ClearMeta(metadata.range.address)) {
|
||||
EXIT("failed to acquire HTile metadata for a depth clear\n");
|
||||
}
|
||||
depth.depth_meta_clear_enable =
|
||||
const bool meta_clear =
|
||||
metadata.kind == ImageMetadataKind::Htile &&
|
||||
cache.IsMetaCleared(metadata.range.address, depth.desc.view_info.base_layer);
|
||||
depth.depth_load_clear_enable = depth.depth_clear_enable || depth.depth_meta_clear_enable;
|
||||
if (depth.depth_meta_clear_enable &&
|
||||
depth.depth_load_clear_enable = depth.depth_clear_enable || meta_clear;
|
||||
if (meta_clear &&
|
||||
!cache.TouchMeta(metadata.range.address, depth.desc.view_info.base_layer, false)) {
|
||||
EXIT("failed to consume HTile clear state\n");
|
||||
}
|
||||
@@ -863,8 +863,8 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ResolvePrimitiveRestart(const CommandBuffer& buffer, vk::PrimitiveTopology topology,
|
||||
uint32_t index_type_and_size) {
|
||||
static bool ResolvePrimitiveRestart(const CommandBuffer& buffer,
|
||||
const DrawIndexBufferSource& source) {
|
||||
const auto control = buffer.GetUserConfig().GetPrimitiveResetControl();
|
||||
EXIT_NOT_IMPLEMENTED((control & ~0x3u) != 0);
|
||||
if ((control & 0x1u) == 0) {
|
||||
@@ -876,35 +876,38 @@ static bool ResolvePrimitiveRestart(const CommandBuffer& buffer, vk::PrimitiveTo
|
||||
case Prospero::PrimitiveType::kTriStrip: break;
|
||||
default: return false;
|
||||
}
|
||||
if (topology != vk::PrimitiveTopology::eLineStrip &&
|
||||
topology != vk::PrimitiveTopology::eTriangleStrip &&
|
||||
topology != vk::PrimitiveTopology::eTriangleFan) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t index_mask = 0;
|
||||
switch (static_cast<Prospero::IndexType>(index_type_and_size)) {
|
||||
case Prospero::IndexType::kIndex8: index_mask = 0xffu; break;
|
||||
case Prospero::IndexType::kIndex16: index_mask = 0xffffu; break;
|
||||
case Prospero::IndexType::kIndex32: index_mask = 0xffffffffu; break;
|
||||
default: EXIT("unknown index_type_and_size: %u\n", index_type_and_size);
|
||||
}
|
||||
|
||||
const auto reset_index = buffer.GetRegisters().GetPrimitiveResetIndex();
|
||||
const auto element_size = source.guest_element_size;
|
||||
const auto index_mask = UINT32_MAX >> ((4 - element_size) * 8);
|
||||
const auto reset_index = buffer.GetRegisters().GetPrimitiveResetIndex();
|
||||
if ((control & 0x2u) != 0 && (reset_index & ~index_mask) != 0) {
|
||||
return false;
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED((reset_index & index_mask) != index_mask);
|
||||
return true;
|
||||
const auto restart_index = reset_index & index_mask;
|
||||
if (restart_index == index_mask) {
|
||||
// Use native restart; the 8-bit path widens its marker to 0xffff.
|
||||
return true;
|
||||
}
|
||||
|
||||
// A game can set a custom reset value without using it in the index buffer.
|
||||
// Keep restart off in that case; fail if we actually find the value.
|
||||
// Scan before preparing draw resources: readback can restart the command buffer.
|
||||
EXIT_NOT_IMPLEMENTED(source.address == 0);
|
||||
const auto* indices = reinterpret_cast<const uint8_t*>(source.address);
|
||||
for (uint64_t offset = 0; offset < source.size; offset += element_size) {
|
||||
uint32_t index = 0;
|
||||
std::memcpy(&index, indices + offset, element_size);
|
||||
EXIT_NOT_IMPLEMENTED(index == restart_index);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RenderExecutor::PrepareDrawRenderState(uint64_t submit_id, CommandBuffer& buffer,
|
||||
const DrawCallInfo& draw,
|
||||
bool RenderExecutor::PrepareDrawRenderState(CommandBuffer& buffer, const DrawCallInfo& draw,
|
||||
uint32_t render_target_slice_offset,
|
||||
bool log_setup_phases, DrawRenderState& state) {
|
||||
auto& ctx = buffer.GetRegisters();
|
||||
|
||||
if (ResolveColorTargets(submit_id, buffer, render_target_slice_offset)) {
|
||||
if (ResolveColorTargets(buffer, render_target_slice_offset)) {
|
||||
return false;
|
||||
}
|
||||
if (log_setup_phases) {
|
||||
@@ -913,7 +916,7 @@ bool RenderExecutor::PrepareDrawRenderState(uint64_t submit_id, CommandBuffer& b
|
||||
for (uint32_t slot = 0; slot < RENDER_COLOR_ATTACHMENTS_MAX; slot++) {
|
||||
if (slot == 0 || (render_target_mask_slot(ctx.GetRenderTargetMask(), slot) != 0 &&
|
||||
ctx.GetRenderTarget(slot).base.addr != 0)) {
|
||||
ResolveRenderColorTarget(submit_id, buffer, state.color_info[state.color_count],
|
||||
ResolveRenderColorTarget(buffer, state.color_info[state.color_count],
|
||||
render_target_slice_offset, slot);
|
||||
if (state.color_info[state.color_count].image_id) {
|
||||
state.color_count++;
|
||||
@@ -923,7 +926,7 @@ bool RenderExecutor::PrepareDrawRenderState(uint64_t submit_id, CommandBuffer& b
|
||||
if (log_setup_phases) {
|
||||
LogDrawPhase(draw.name, "ResolveRenderDepthTarget");
|
||||
}
|
||||
ResolveRenderDepthTarget(submit_id, buffer, state.depth_info);
|
||||
ResolveRenderDepthTarget(buffer, state.depth_info);
|
||||
|
||||
state.ps_active = DrawHasActivePixelShader(buffer);
|
||||
if (state.color_count == 0 && !state.depth_info.image_id && !state.ps_active) {
|
||||
@@ -1270,53 +1273,41 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
|
||||
return;
|
||||
}
|
||||
|
||||
vk::IndexType index_type = vk::IndexType::eUint16;
|
||||
uint64_t index_size = 0;
|
||||
bool expand_index8_to_u16 = false;
|
||||
const bool primitive_restart =
|
||||
ResolvePrimitiveRestart(buffer, topology, args.index_type_and_size);
|
||||
|
||||
DrawIndexBufferSource index_source {};
|
||||
index_source.address = reinterpret_cast<uint64_t>(args.index_addr);
|
||||
switch (static_cast<Prospero::IndexType>(args.index_type_and_size)) {
|
||||
case Prospero::IndexType::kIndex16:
|
||||
index_type = vk::IndexType::eUint16;
|
||||
index_size = 2 * static_cast<uint64_t>(args.index_count);
|
||||
index_source.type = vk::IndexType::eUint16;
|
||||
index_source.guest_element_size = 2;
|
||||
break;
|
||||
case Prospero::IndexType::kIndex32:
|
||||
index_type = vk::IndexType::eUint32;
|
||||
index_size = 4 * static_cast<uint64_t>(args.index_count);
|
||||
index_source.type = vk::IndexType::eUint32;
|
||||
index_source.guest_element_size = 4;
|
||||
break;
|
||||
// Some games use it - need vulkan extension
|
||||
case Prospero::IndexType::kIndex8:
|
||||
index_type = vk::IndexType::eUint16;
|
||||
index_size = static_cast<uint64_t>(args.index_count);
|
||||
expand_index8_to_u16 = true;
|
||||
index_source.guest_element_size = 1;
|
||||
break;
|
||||
default: EXIT("unknown index_type_and_size: %u\n", args.index_type_and_size);
|
||||
}
|
||||
index_source.size = static_cast<uint64_t>(args.index_count) * index_source.guest_element_size;
|
||||
const bool primitive_restart = ResolvePrimitiveRestart(buffer, index_source);
|
||||
|
||||
const DrawCallInfo draw {"DrawIndex", CommandBufferDebugOp::DrawIndex, args.index_count,
|
||||
args.instance_count, args.first_instance};
|
||||
std::vector<uint16_t> expanded_indices;
|
||||
if (expand_index8_to_u16) {
|
||||
if (index_source.guest_element_size == 1) {
|
||||
EXIT_NOT_IMPLEMENTED(args.index_addr == nullptr);
|
||||
const auto* src = static_cast<const uint8_t*>(args.index_addr);
|
||||
expanded_indices.resize(args.index_count);
|
||||
for (uint32_t i = 0; i < args.index_count; i++) {
|
||||
expanded_indices[i] = primitive_restart && src[i] == 0xffu ? 0xffffu : src[i];
|
||||
}
|
||||
index_source.host_data = expanded_indices.data();
|
||||
index_source.size = expanded_indices.size() * sizeof(uint16_t);
|
||||
}
|
||||
|
||||
DrawIndexBufferSource index_source {};
|
||||
index_source.address = reinterpret_cast<uint64_t>(args.index_addr);
|
||||
index_source.host_data =
|
||||
expanded_indices.empty() ? nullptr : static_cast<const void*>(expanded_indices.data());
|
||||
index_source.size =
|
||||
expanded_indices.empty() ? index_size : expanded_indices.size() * sizeof(uint16_t);
|
||||
index_source.type = index_type;
|
||||
index_source.guest_element_size = static_cast<uint32_t>(index_size / args.index_count);
|
||||
|
||||
const DrawCallInfo draw {"DrawIndex", CommandBufferDebugOp::DrawIndex, args.index_count,
|
||||
args.instance_count, args.first_instance};
|
||||
DrawRenderState state {};
|
||||
if (!PrepareDrawRenderState(submit_id, buffer, draw, args.render_target_slice_offset, true,
|
||||
if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, true,
|
||||
state)) {
|
||||
ResetBindings();
|
||||
return;
|
||||
@@ -1393,7 +1384,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D
|
||||
args.vertex_count, args.instance_count, args.first_instance};
|
||||
|
||||
DrawRenderState state {};
|
||||
if (!PrepareDrawRenderState(submit_id, buffer, draw, args.render_target_slice_offset, false,
|
||||
if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, false,
|
||||
state)) {
|
||||
ResetBindings();
|
||||
return;
|
||||
@@ -1440,8 +1431,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D
|
||||
ResetBindings();
|
||||
}
|
||||
|
||||
bool RenderExecutor::ResolveColorTargets(uint64_t submit_id, CommandBuffer& buffer,
|
||||
uint32_t render_target_slice_offset) {
|
||||
bool RenderExecutor::ResolveColorTargets(CommandBuffer& buffer, uint32_t render_target_slice_offset) {
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
if (hw.GetColorControl().mode != 3) {
|
||||
return false;
|
||||
@@ -1455,8 +1445,8 @@ bool RenderExecutor::ResolveColorTargets(uint64_t submit_id, CommandBuffer& buff
|
||||
|
||||
RenderColorInfo src {};
|
||||
RenderColorInfo dst {};
|
||||
ResolveRenderColorTarget(submit_id, buffer, src, render_target_slice_offset, 0, true, true);
|
||||
ResolveRenderColorTarget(submit_id, buffer, dst, render_target_slice_offset, 1, true, true);
|
||||
ResolveRenderColorTarget(buffer, src, render_target_slice_offset, 0, true, true);
|
||||
ResolveRenderColorTarget(buffer, dst, render_target_slice_offset, 1, true, true);
|
||||
if (!src.image_id || !dst.image_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -130,35 +130,42 @@ uint64_t GraphicContext::GetTotalMemoryBudget() const {
|
||||
|
||||
bool GraphicContext::CreateImage(const vk::ImageCreateInfo& image_info, VulkanImage& image) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(allocator == nullptr || image.image != nullptr || image.memory.allocation != nullptr);
|
||||
EXIT_IF(allocator == nullptr || image.image != nullptr || image.allocation != nullptr);
|
||||
|
||||
auto& memory = image.memory;
|
||||
VmaAllocationCreateInfo alloc_info {};
|
||||
alloc_info.requiredFlags = static_cast<vk::MemoryPropertyFlags::MaskType>(memory.property);
|
||||
alloc_info.preferredFlags =
|
||||
static_cast<vk::MemoryPropertyFlags::MaskType>(memory.preferred_property);
|
||||
alloc_info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
|
||||
vk::Image::CType native_image = VK_NULL_HANDLE;
|
||||
const auto result = static_cast<vk::Result>(
|
||||
vmaCreateImage(allocator, static_cast<const vk::ImageCreateInfo::NativeType*>(image_info),
|
||||
&alloc_info, &native_image, &memory.allocation, nullptr));
|
||||
&alloc_info, &native_image, &image.allocation, nullptr));
|
||||
image.image = native_image;
|
||||
if (result != vk::Result::eSuccess) {
|
||||
LogMemoryBudget();
|
||||
return false;
|
||||
}
|
||||
|
||||
image.format = image_info.format;
|
||||
image.image_type = image_info.imageType;
|
||||
image.extent = image_info.extent;
|
||||
image.layers = image_info.arrayLayers;
|
||||
image.mip_levels = image_info.mipLevels;
|
||||
image.samples = static_cast<uint32_t>(image_info.samples);
|
||||
image.usage = image_info.usage;
|
||||
image.flags = image_info.flags;
|
||||
image.state = {.layout = image_info.initialLayout};
|
||||
image.subresource_states.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphicContext::DeleteImage(VulkanImage& image) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(allocator == nullptr || image.image == nullptr || image.memory.allocation == nullptr);
|
||||
EXIT_IF(allocator == nullptr || image.image == nullptr || image.allocation == nullptr);
|
||||
|
||||
auto& memory = image.memory;
|
||||
vmaDestroyImage(allocator, image.image, memory.allocation);
|
||||
image.image = nullptr;
|
||||
memory.allocation = nullptr;
|
||||
vmaDestroyImage(allocator, image.image, image.allocation);
|
||||
image.image = nullptr;
|
||||
image.allocation = nullptr;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -191,24 +191,15 @@ void Presenter::Frame::Configure(GraphicContext& graphics, vk::Extent2D extent,
|
||||
}
|
||||
if (dst.image != nullptr) {
|
||||
graphics.DeleteImage(dst);
|
||||
dst.memory = {};
|
||||
}
|
||||
|
||||
dst.extent = {extent.width, extent.height, 1};
|
||||
dst.format = format;
|
||||
dst.layers = 1;
|
||||
dst.mip_levels = 1;
|
||||
dst.state = {};
|
||||
dst.subresource_states.clear();
|
||||
dst.memory.property = vk::MemoryPropertyFlagBits::eDeviceLocal;
|
||||
|
||||
vk::ImageCreateInfo create {};
|
||||
create.sType = vk::StructureType::eImageCreateInfo;
|
||||
create.imageType = vk::ImageType::e2D;
|
||||
create.extent = {dst.extent.width, dst.extent.height, 1};
|
||||
create.extent = {extent.width, extent.height, 1};
|
||||
create.mipLevels = 1;
|
||||
create.arrayLayers = 1;
|
||||
create.format = dst.format;
|
||||
create.format = format;
|
||||
create.tiling = vk::ImageTiling::eOptimal;
|
||||
create.initialLayout = vk::ImageLayout::eUndefined;
|
||||
create.usage = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
|
||||
@@ -216,7 +207,7 @@ void Presenter::Frame::Configure(GraphicContext& graphics, vk::Extent2D extent,
|
||||
create.samples = vk::SampleCountFlagBits::e1;
|
||||
if (!graphics.CreateImage(create, dst)) {
|
||||
EXIT("failed to allocate prepared presentation image, extent=%ux%u format=%d\n",
|
||||
dst.extent.width, dst.extent.height, static_cast<int>(dst.format));
|
||||
extent.width, extent.height, static_cast<int>(format));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -637,7 +637,15 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
|
||||
feedback_layout.pNext = &feedback_dynamic;
|
||||
supported_features2.pNext = &feedback_layout;
|
||||
}
|
||||
const bool provoking_extension =
|
||||
HasExtension(device_extensions, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
|
||||
vk::PhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex {};
|
||||
if (provoking_extension) {
|
||||
provoking_vertex.pNext = supported_features2.pNext;
|
||||
supported_features2.pNext = &provoking_vertex;
|
||||
}
|
||||
physical_device.getFeatures2(&supported_features2);
|
||||
graphics.provoking_vertex_last_enabled = provoking_extension && provoking_vertex.provokingVertexLast;
|
||||
graphics.attachment_feedback_loop_enabled =
|
||||
feedback_extensions && feedback_layout.attachmentFeedbackLoopLayout &&
|
||||
feedback_dynamic.attachmentFeedbackLoopDynamicState;
|
||||
@@ -753,6 +761,11 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
|
||||
create_info.pNext = graphics.attachment_feedback_loop_enabled
|
||||
? static_cast<void*>(&feedback_layout)
|
||||
: feedback_dynamic.pNext;
|
||||
if (graphics.provoking_vertex_last_enabled) {
|
||||
provoking_vertex.pNext = const_cast<void*>(create_info.pNext);
|
||||
provoking_vertex.transformFeedbackPreservesProvokingVertex = VK_FALSE;
|
||||
create_info.pNext = &provoking_vertex;
|
||||
}
|
||||
create_info.flags = {};
|
||||
create_info.pQueueCreateInfos = &queue_create_info;
|
||||
create_info.queueCreateInfoCount = 1;
|
||||
@@ -1145,6 +1158,7 @@ void WindowContext::CreateVulkan() {
|
||||
graphic_ctx.memory_budget_ext_enabled = true;
|
||||
}
|
||||
for (const auto* extension: {VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
|
||||
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME,
|
||||
VK_EXT_MESH_SHADER_EXTENSION_NAME,
|
||||
VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME}) {
|
||||
if (HasExtension(available_extensions, extension)) {
|
||||
|
||||
@@ -1016,7 +1016,7 @@ void WindowContext::UpdateTitle() {
|
||||
|
||||
const auto* device_name = graphic_ctx.GetPhysicalDeviceProperties().deviceName.data();
|
||||
auto text = fmt::format(
|
||||
"[{} | {}] {}{}{}{}{}{}[{}] [{}], frame: {}, fps: {:f}", KYTY_BUILD_LABEL, build_type,
|
||||
"[{} | {}] {}{}{}{}{}{}[{}] [{}], frame: {}, fps: {:.0f}", KYTY_BUILD_LABEL, build_type,
|
||||
(has_title ? title : ""), (has_title ? ", " : ""), (has_title_id ? title_id : ""),
|
||||
(has_title_id ? ", " : ""), (has_app_ver ? app_ver : ""), (has_app_ver ? " " : ""),
|
||||
device_name, processor_name, frame_num, current_fps);
|
||||
|
||||
@@ -238,28 +238,13 @@ constexpr BufferFormatInfo GetFormatInfo(Prospero::BufferFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsKnownFormat(Prospero::BufferFormat format) {
|
||||
return GetFormatInfo(format).type != ComponentType::Unknown;
|
||||
}
|
||||
|
||||
constexpr uint32_t GetFormatComponentCount(Prospero::BufferFormat format) {
|
||||
return GetFormatInfo(format).component_count;
|
||||
}
|
||||
|
||||
constexpr uint32_t GetFormatComponentByteOffset(Prospero::BufferFormat format, uint32_t component) {
|
||||
const auto info = GetFormatInfo(format);
|
||||
constexpr uint32_t GetFormatComponentByteOffset(const BufferFormatInfo& info, uint32_t component) {
|
||||
if (component >= info.component_count) {
|
||||
return 0;
|
||||
}
|
||||
return info.packed_bitfield ? 0u : info.component_bit_offset[component] / 8u;
|
||||
}
|
||||
|
||||
constexpr bool CanUseTypedBufferLoad(Prospero::BufferFormat format) {
|
||||
const auto info = GetFormatInfo(format);
|
||||
return info.type != ComponentType::Unknown && !info.packed_bitfield &&
|
||||
info.component_bits[0] == 32u;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Format
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_BUFFERFORMAT_H_ */
|
||||
@@ -77,8 +77,7 @@ enum class EmbeddedFetchValueType {
|
||||
AttribTable,
|
||||
Attrib,
|
||||
BufferTable,
|
||||
Buffer,
|
||||
Index
|
||||
Buffer
|
||||
};
|
||||
|
||||
struct EmbeddedFetchSgprInfo {
|
||||
@@ -88,10 +87,6 @@ struct EmbeddedFetchSgprInfo {
|
||||
std::vector<uint32_t> prolog_loads;
|
||||
};
|
||||
|
||||
struct EmbeddedFetchVgprInfo {
|
||||
EmbeddedFetchValueType type = EmbeddedFetchValueType::Unknown;
|
||||
};
|
||||
|
||||
using EmbeddedFetchVectorLanes = std::map<uint64_t, EmbeddedFetchSgprInfo>;
|
||||
|
||||
uint64_t EmbeddedFetchVectorLaneKey(uint32_t reg, uint32_t lane) {
|
||||
@@ -108,7 +103,6 @@ void ClearEmbeddedFetchVectorLanes(EmbeddedFetchVectorLanes* lanes, uint32_t reg
|
||||
lanes->erase(first, last);
|
||||
}
|
||||
|
||||
using EmbeddedFetchLoad = Frontend::EmbeddedFetchLoad;
|
||||
using EmbeddedFetchData = Frontend::EmbeddedFetchPlan;
|
||||
|
||||
bool IsDecodedSgpr(const Decoder::Operand& op) {
|
||||
@@ -244,7 +238,7 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
|
||||
const int buffer_reg = input_info->fetch_buffer_reg + shift_regs;
|
||||
|
||||
std::array<EmbeddedFetchSgprInfo, 108> sgprs {};
|
||||
std::array<EmbeddedFetchVgprInfo, 256> vgprs {};
|
||||
std::array<bool, 256> vgpr_is_index {};
|
||||
EmbeddedFetchVectorLanes vector_lanes;
|
||||
const bool track_vector_lanes =
|
||||
std::none_of(decoded.instructions.begin(), decoded.instructions.end(),
|
||||
@@ -299,8 +293,8 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_WRITELANE_B32: {
|
||||
uint32_t lane = 0;
|
||||
if (IsDecodedVgpr(inst.dst) && inst.dst.reg < vgprs.size()) {
|
||||
vgprs[inst.dst.reg] = {};
|
||||
if (IsDecodedVgpr(inst.dst) && inst.dst.reg < vgpr_is_index.size()) {
|
||||
vgpr_is_index[inst.dst.reg] = false;
|
||||
}
|
||||
if (track_vector_lanes && IsDecodedVgpr(inst.dst) && IsDecodedSgpr(inst.src0) &&
|
||||
DecodedSgprReg(inst.src0) < sgprs.size() &&
|
||||
@@ -405,13 +399,13 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
|
||||
ClearEmbeddedFetchSgprs(sgprs, inst.dst, DecodedDstSize(inst));
|
||||
}
|
||||
} else if (inst.opcode == Decoder::Opcode::V_CNDMASK_B32) {
|
||||
if (IsDecodedVgpr(inst.dst) && inst.dst.reg < vgprs.size()) {
|
||||
if (IsDecodedVgpr(inst.dst) && inst.dst.reg < vgpr_is_index.size()) {
|
||||
ClearEmbeddedFetchVectorLanes(&vector_lanes, inst.dst.reg);
|
||||
}
|
||||
if (IsDecodedVgpr(inst.dst) && inst.dst.reg < vgprs.size() &&
|
||||
if (IsDecodedVgpr(inst.dst) && inst.dst.reg < vgpr_is_index.size() &&
|
||||
IsDecodedVgpr(inst.src0) && inst.src0.reg == 8 &&
|
||||
IsDecodedVgpr(inst.src1) && inst.src1.reg == 5) {
|
||||
vgprs[inst.dst.reg].type = EmbeddedFetchValueType::Index;
|
||||
vgpr_is_index[inst.dst.reg] = true;
|
||||
}
|
||||
} else if (IsEmbeddedFetchAttribPropagationAlu(inst)) {
|
||||
if (IsDecodedSgpr(inst.dst) && IsDecodedSgpr(inst.src0) &&
|
||||
@@ -441,16 +435,11 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
|
||||
}
|
||||
}
|
||||
} else if (IsEmbeddedFetchBufferLoad(inst)) {
|
||||
if (IsDecodedVgpr(inst.src0) && inst.src0.reg < vgprs.size() &&
|
||||
vgprs[inst.src0.reg].type == EmbeddedFetchValueType::Index &&
|
||||
if (IsDecodedVgpr(inst.src0) && inst.src0.reg < vgpr_is_index.size() &&
|
||||
vgpr_is_index[inst.src0.reg] &&
|
||||
IsDecodedSgpr(inst.src1) && DecodedSgprReg(inst.src1) < sgprs.size() &&
|
||||
sgprs[DecodedSgprReg(inst.src1)].type == EmbeddedFetchValueType::Buffer) {
|
||||
const auto& buffer = sgprs[DecodedSgprReg(inst.src1)];
|
||||
EmbeddedFetchLoad load;
|
||||
load.pc = inst.pc;
|
||||
load.attrib_id = buffer.attrib_id;
|
||||
load.components = DecodedDstSize(inst);
|
||||
load.prolog_loads = buffer.prolog_loads;
|
||||
const auto& buffer = sgprs[DecodedSgprReg(inst.src1)];
|
||||
if (data.loads.empty()) {
|
||||
if (!vertex_offset_conflict) {
|
||||
data.vertex_offset_sgpr = vertex_offset_candidate;
|
||||
@@ -459,7 +448,11 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
|
||||
data.instance_offset_sgpr = instance_offset_candidate;
|
||||
}
|
||||
}
|
||||
data.loads.push_back(load);
|
||||
auto& load = data.loads.emplace_back();
|
||||
load.pc = inst.pc;
|
||||
load.attrib_id = buffer.attrib_id;
|
||||
load.components = DecodedDstSize(inst);
|
||||
load.prolog_loads = buffer.prolog_loads;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -467,7 +460,8 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
|
||||
if (inst.opcode == Decoder::Opcode::V_MOVRELD_B32) {
|
||||
vector_lanes.clear();
|
||||
} else if (inst.opcode != Decoder::Opcode::V_WRITELANE_B32 && IsDecodedVgpr(inst.dst)) {
|
||||
for (uint32_t i = 0; i < EmbeddedFetchDstSize(inst) && inst.dst.reg + i < vgprs.size();
|
||||
for (uint32_t i = 0;
|
||||
i < EmbeddedFetchDstSize(inst) && inst.dst.reg + i < vgpr_is_index.size();
|
||||
i++) {
|
||||
ClearEmbeddedFetchVectorLanes(&vector_lanes, inst.dst.reg + i);
|
||||
}
|
||||
@@ -541,7 +535,7 @@ TranslateResult TranslateProgram(std::span<const uint32_t> code, const CompileOp
|
||||
|
||||
Decoder::Program decoded;
|
||||
std::vector<uint32_t> joined_code;
|
||||
if (options.stage == ShaderType::Mesh) {
|
||||
if (!options.back_code.empty()) {
|
||||
decoded = DecodeFusedProgram(code, options.back_code, joined_code);
|
||||
} else {
|
||||
Decoder::DecodeProgram(code, decoded);
|
||||
@@ -664,11 +658,6 @@ TranslateResult TranslateProgram(std::span<const uint32_t> code, const CompileOp
|
||||
IR::EliminateDeadCode(ir.blocks);
|
||||
IR::TrackResources(ir);
|
||||
IR::EliminateDeadCode(ir.blocks);
|
||||
if (options.stage == ShaderType::Vertex) {
|
||||
ir.info.vertex_offset_sgpr = embedded_fetch.vertex_offset_sgpr;
|
||||
ir.info.instance_offset_sgpr = embedded_fetch.instance_offset_sgpr;
|
||||
}
|
||||
|
||||
TranslateResult result;
|
||||
result.program = std::move(ir);
|
||||
if (options.dump_ir) {
|
||||
|
||||
@@ -319,8 +319,7 @@ std::vector<uint32_t> EmitProgram(const IR::Program& program,
|
||||
ValidateNativeProgram(program);
|
||||
IR::ValidateProgram(program, true);
|
||||
EmitterState state(program, input_info);
|
||||
state.stage = program.stage;
|
||||
state.wave_size = program.wave_size;
|
||||
state.stage = program.stage;
|
||||
const auto* workgroup = ShaderWorkgroupInput(program.stage, input_info);
|
||||
state.lane_count =
|
||||
workgroup != nullptr && program.wave_size == 64u && workgroup->host_subgroup_size == 32u
|
||||
@@ -333,7 +332,7 @@ std::vector<uint32_t> EmitProgram(const IR::Program& program,
|
||||
AllocateInputVariables(state);
|
||||
AllocateOutputVariables(state);
|
||||
DefineModule(state);
|
||||
EmitProgram(state, program);
|
||||
EmitProgram(state);
|
||||
state.builder.AddEntryPoint(ExecutionModelForStage(state.stage), state.main_func, "main",
|
||||
state.interface_variables);
|
||||
|
||||
|
||||
@@ -10,26 +10,6 @@ struct Pair {
|
||||
uint32_t high = 0;
|
||||
};
|
||||
|
||||
uint32_t NewUnary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t value) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, value});
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t NewBinary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t lhs,
|
||||
uint32_t rhs) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, lhs, rhs});
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t NewSelect(EmitterState& state, uint32_t type, uint32_t condition, uint32_t true_value,
|
||||
uint32_t false_value) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpSelect, type, result, condition, true_value, false_value});
|
||||
return result;
|
||||
}
|
||||
|
||||
Pair ExtractPair(EmitterState& state, uint32_t value) {
|
||||
Pair result {state.builder.AllocateId(), state.builder.AllocateId()};
|
||||
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), result.low, value, 0});
|
||||
@@ -45,20 +25,20 @@ uint32_t MakePair(EmitterState& state, uint32_t low, uint32_t high) {
|
||||
|
||||
uint32_t CompareEqual64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value,
|
||||
bool not_equal) {
|
||||
const auto compare = NewBinary(state, not_equal ? OpINotEqual : OpIEqual,
|
||||
const auto compare = Binary(state, not_equal ? OpINotEqual : OpIEqual,
|
||||
TypeBoolVector(state, 2), lhs_value, rhs_value);
|
||||
return NewUnary(state, not_equal ? OpAny : OpAll, TypeBool(state), compare);
|
||||
return Unary(state, not_equal ? OpAny : OpAll, TypeBool(state), compare);
|
||||
}
|
||||
|
||||
uint32_t CompareOrdered64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value,
|
||||
uint32_t high_compare, uint32_t low_compare) {
|
||||
const auto lhs = ExtractPair(state, lhs_value);
|
||||
const auto rhs = ExtractPair(state, rhs_value);
|
||||
const auto high_equal = NewBinary(state, OpIEqual, TypeBool(state), lhs.high, rhs.high);
|
||||
const auto high_result = NewBinary(state, high_compare, TypeBool(state), lhs.high, rhs.high);
|
||||
const auto low_result = NewBinary(state, low_compare, TypeBool(state), lhs.low, rhs.low);
|
||||
const auto low_path = NewBinary(state, OpLogicalAnd, TypeBool(state), high_equal, low_result);
|
||||
return NewBinary(state, OpLogicalOr, TypeBool(state), high_result, low_path);
|
||||
const auto high_equal = Binary(state, OpIEqual, TypeBool(state), lhs.high, rhs.high);
|
||||
const auto high_result = Binary(state, high_compare, TypeBool(state), lhs.high, rhs.high);
|
||||
const auto low_result = Binary(state, low_compare, TypeBool(state), lhs.low, rhs.low);
|
||||
const auto low_path = Binary(state, OpLogicalAnd, TypeBool(state), high_equal, low_result);
|
||||
return Binary(state, OpLogicalOr, TypeBool(state), high_result, low_path);
|
||||
}
|
||||
|
||||
uint32_t EmitAdd64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value) {
|
||||
@@ -70,20 +50,20 @@ uint32_t EmitAdd64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value)
|
||||
state.builder.AddFunction({OpIAddCarry, TypeU32Pair(state), low_pair, lhs.low, rhs.low});
|
||||
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), low, low_pair, 0});
|
||||
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), carry, low_pair, 1});
|
||||
const auto high0 = NewBinary(state, OpIAdd, TypeU32(state), lhs.high, rhs.high);
|
||||
const auto high = NewBinary(state, OpIAdd, TypeU32(state), high0, carry);
|
||||
const auto high0 = Binary(state, OpIAdd, TypeU32(state), lhs.high, rhs.high);
|
||||
const auto high = Binary(state, OpIAdd, TypeU32(state), high0, carry);
|
||||
return MakePair(state, low, high);
|
||||
}
|
||||
|
||||
uint32_t EmitSub64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value) {
|
||||
const auto lhs = ExtractPair(state, lhs_value);
|
||||
const auto rhs = ExtractPair(state, rhs_value);
|
||||
const auto low = NewBinary(state, OpISub, TypeU32(state), lhs.low, rhs.low);
|
||||
const auto borrow = NewBinary(state, OpULessThan, TypeBool(state), lhs.low, rhs.low);
|
||||
const auto low = Binary(state, OpISub, TypeU32(state), lhs.low, rhs.low);
|
||||
const auto borrow = Binary(state, OpULessThan, TypeBool(state), lhs.low, rhs.low);
|
||||
const auto borrow_u32 =
|
||||
NewSelect(state, TypeU32(state), borrow, ConstantU32(state, 1), ConstantU32(state, 0));
|
||||
const auto high0 = NewBinary(state, OpISub, TypeU32(state), lhs.high, rhs.high);
|
||||
const auto high = NewBinary(state, OpISub, TypeU32(state), high0, borrow_u32);
|
||||
Select(state, TypeU32(state), borrow, ConstantU32(state, 1), ConstantU32(state, 0));
|
||||
const auto high0 = Binary(state, OpISub, TypeU32(state), lhs.high, rhs.high);
|
||||
const auto high = Binary(state, OpISub, TypeU32(state), high0, borrow_u32);
|
||||
return MakePair(state, low, high);
|
||||
}
|
||||
|
||||
@@ -93,27 +73,27 @@ uint32_t EmitMulHigh(EmitterState& state, uint32_t lhs, uint32_t rhs, bool signe
|
||||
uint32_t lhs_operand = lhs;
|
||||
uint32_t rhs_operand = rhs;
|
||||
if (signed_value) {
|
||||
lhs_operand = NewUnary(state, OpBitcast, TypeI32(state), lhs);
|
||||
rhs_operand = NewUnary(state, OpBitcast, TypeI32(state), rhs);
|
||||
lhs_operand = Unary(state, OpBitcast, TypeI32(state), lhs);
|
||||
rhs_operand = Unary(state, OpBitcast, TypeI32(state), rhs);
|
||||
}
|
||||
const auto extended = state.builder.AllocateId();
|
||||
state.builder.AddFunction({signed_value ? OpSMulExtended : OpUMulExtended, pair_type, extended,
|
||||
lhs_operand, rhs_operand});
|
||||
const auto high = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeExtract, operand_type, high, extended, 1});
|
||||
return signed_value ? NewUnary(state, OpBitcast, TypeU32(state), high) : high;
|
||||
return signed_value ? Unary(state, OpBitcast, TypeU32(state), high) : high;
|
||||
}
|
||||
|
||||
uint32_t EmitMul64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value) {
|
||||
const auto lhs = ExtractPair(state, lhs_value);
|
||||
const auto rhs = ExtractPair(state, rhs_value);
|
||||
const auto low = NewBinary(state, OpIMul, TypeU32(state), lhs.low, rhs.low);
|
||||
const auto low = Binary(state, OpIMul, TypeU32(state), lhs.low, rhs.low);
|
||||
const auto high0 = EmitMulHigh(state, lhs.low, rhs.low, false);
|
||||
const auto high1 = NewBinary(state, OpIMul, TypeU32(state), lhs.low, rhs.high);
|
||||
const auto high2 = NewBinary(state, OpIMul, TypeU32(state), lhs.high, rhs.low);
|
||||
const auto high1 = Binary(state, OpIMul, TypeU32(state), lhs.low, rhs.high);
|
||||
const auto high2 = Binary(state, OpIMul, TypeU32(state), lhs.high, rhs.low);
|
||||
return MakePair(state, low,
|
||||
NewBinary(state, OpIAdd, TypeU32(state),
|
||||
NewBinary(state, OpIAdd, TypeU32(state), high0, high1), high2));
|
||||
Binary(state, OpIAdd, TypeU32(state),
|
||||
Binary(state, OpIAdd, TypeU32(state), high0, high1), high2));
|
||||
}
|
||||
|
||||
uint32_t EmitShift64(EmitterState& state, uint32_t opcode, uint32_t value, uint32_t shift) {
|
||||
@@ -126,33 +106,33 @@ uint32_t EmitShift64(EmitterState& state, uint32_t opcode, uint32_t value, uint3
|
||||
EmitShiftRightLogicalU64Values(state, pair.low, pair.high, shift, low, high);
|
||||
} else {
|
||||
const auto amount =
|
||||
NewBinary(state, OpBitwiseAnd, TypeU32(state), shift, ConstantU32(state, 63));
|
||||
Binary(state, OpBitwiseAnd, TypeU32(state), shift, ConstantU32(state, 63));
|
||||
const auto word_shift =
|
||||
NewBinary(state, OpBitwiseAnd, TypeU32(state), amount, ConstantU32(state, 31));
|
||||
Binary(state, OpBitwiseAnd, TypeU32(state), amount, ConstantU32(state, 31));
|
||||
const auto at_least_32 =
|
||||
NewBinary(state, OpUGreaterThanEqual, TypeBool(state), amount, ConstantU32(state, 32));
|
||||
Binary(state, OpUGreaterThanEqual, TypeBool(state), amount, ConstantU32(state, 32));
|
||||
const auto nonzero =
|
||||
NewBinary(state, OpINotEqual, TypeBool(state), amount, ConstantU32(state, 0));
|
||||
Binary(state, OpINotEqual, TypeBool(state), amount, ConstantU32(state, 0));
|
||||
const auto high_shifted =
|
||||
NewBinary(state, OpShiftRightArithmetic, TypeU32(state), pair.high, word_shift);
|
||||
Binary(state, OpShiftRightArithmetic, TypeU32(state), pair.high, word_shift);
|
||||
const auto low_shifted =
|
||||
NewBinary(state, OpShiftRightLogical, TypeU32(state), pair.low, word_shift);
|
||||
Binary(state, OpShiftRightLogical, TypeU32(state), pair.low, word_shift);
|
||||
const auto carry_count =
|
||||
NewBinary(state, OpBitwiseAnd, TypeU32(state),
|
||||
NewBinary(state, OpISub, TypeU32(state), ConstantU32(state, 32), word_shift),
|
||||
Binary(state, OpBitwiseAnd, TypeU32(state),
|
||||
Binary(state, OpISub, TypeU32(state), ConstantU32(state, 32), word_shift),
|
||||
ConstantU32(state, 31));
|
||||
const auto carry =
|
||||
NewSelect(state, TypeU32(state), nonzero,
|
||||
NewBinary(state, OpShiftLeftLogical, TypeU32(state), pair.high, carry_count),
|
||||
Select(state, TypeU32(state), nonzero,
|
||||
Binary(state, OpShiftLeftLogical, TypeU32(state), pair.high, carry_count),
|
||||
ConstantU32(state, 0));
|
||||
const auto low_below_32 = NewBinary(state, OpBitwiseOr, TypeU32(state), low_shifted, carry);
|
||||
const auto low_below_32 = Binary(state, OpBitwiseOr, TypeU32(state), low_shifted, carry);
|
||||
const auto negative =
|
||||
NewBinary(state, OpSLessThan, TypeBool(state), pair.high, ConstantU32(state, 0));
|
||||
const auto sign_fill = NewSelect(state, TypeU32(state), negative,
|
||||
Binary(state, OpSLessThan, TypeBool(state), pair.high, ConstantU32(state, 0));
|
||||
const auto sign_fill = Select(state, TypeU32(state), negative,
|
||||
ConstantU32(state, 0xffffffffu), ConstantU32(state, 0));
|
||||
return MakePair(state,
|
||||
NewSelect(state, TypeU32(state), at_least_32, high_shifted, low_below_32),
|
||||
NewSelect(state, TypeU32(state), at_least_32, sign_fill, high_shifted));
|
||||
Select(state, TypeU32(state), at_least_32, high_shifted, low_below_32),
|
||||
Select(state, TypeU32(state), at_least_32, sign_fill, high_shifted));
|
||||
}
|
||||
return MakePair(state, low, high);
|
||||
}
|
||||
@@ -165,36 +145,36 @@ uint32_t EmitConstantShift64(EmitterState& state, uint32_t opcode, uint32_t valu
|
||||
const auto pair = ExtractPair(state, value);
|
||||
if (opcode == OpShiftLeftLogical) {
|
||||
if (shift < 32u) {
|
||||
const auto low = NewBinary(state, OpShiftLeftLogical, TypeU32(state), pair.low,
|
||||
const auto low = Binary(state, OpShiftLeftLogical, TypeU32(state), pair.low,
|
||||
ConstantU32(state, shift));
|
||||
const auto high = NewBinary(state, OpBitwiseOr, TypeU32(state),
|
||||
NewBinary(state, OpShiftLeftLogical, TypeU32(state),
|
||||
const auto high = Binary(state, OpBitwiseOr, TypeU32(state),
|
||||
Binary(state, OpShiftLeftLogical, TypeU32(state),
|
||||
pair.high, ConstantU32(state, shift)),
|
||||
NewBinary(state, OpShiftRightLogical, TypeU32(state),
|
||||
Binary(state, OpShiftRightLogical, TypeU32(state),
|
||||
pair.low, ConstantU32(state, 32u - shift)));
|
||||
return MakePair(state, low, high);
|
||||
}
|
||||
return MakePair(state, ConstantU32(state, 0),
|
||||
shift == 32u ? pair.low
|
||||
: NewBinary(state, OpShiftLeftLogical, TypeU32(state),
|
||||
: Binary(state, OpShiftLeftLogical, TypeU32(state),
|
||||
pair.low, ConstantU32(state, shift - 32u)));
|
||||
}
|
||||
if (shift < 32u) {
|
||||
const auto low = NewBinary(state, OpBitwiseOr, TypeU32(state),
|
||||
NewBinary(state, OpShiftRightLogical, TypeU32(state), pair.low,
|
||||
const auto low = Binary(state, OpBitwiseOr, TypeU32(state),
|
||||
Binary(state, OpShiftRightLogical, TypeU32(state), pair.low,
|
||||
ConstantU32(state, shift)),
|
||||
NewBinary(state, OpShiftLeftLogical, TypeU32(state), pair.high,
|
||||
Binary(state, OpShiftLeftLogical, TypeU32(state), pair.high,
|
||||
ConstantU32(state, 32u - shift)));
|
||||
const auto high =
|
||||
NewBinary(state, opcode, TypeU32(state), pair.high, ConstantU32(state, shift));
|
||||
Binary(state, opcode, TypeU32(state), pair.high, ConstantU32(state, shift));
|
||||
return MakePair(state, low, high);
|
||||
}
|
||||
const auto high = opcode == OpShiftRightArithmetic
|
||||
? NewBinary(state, OpShiftRightArithmetic, TypeU32(state), pair.high,
|
||||
? Binary(state, OpShiftRightArithmetic, TypeU32(state), pair.high,
|
||||
ConstantU32(state, 31u))
|
||||
: ConstantU32(state, 0);
|
||||
const auto low = shift == 32u ? pair.high
|
||||
: NewBinary(state, opcode, TypeU32(state), pair.high,
|
||||
: Binary(state, opcode, TypeU32(state), pair.high,
|
||||
ConstantU32(state, shift - 32u));
|
||||
return MakePair(state, low, high);
|
||||
}
|
||||
@@ -207,12 +187,12 @@ uint32_t EmitFindMsb64(EmitterState& state, uint32_t value) {
|
||||
{OpExtInst, TypeI32(state), high_i, GlslStd450(state), GlslFindUMsb, pair.high});
|
||||
state.builder.AddFunction(
|
||||
{OpExtInst, TypeI32(state), low_i, GlslStd450(state), GlslFindUMsb, pair.low});
|
||||
const auto high = NewUnary(state, OpBitcast, TypeU32(state), high_i);
|
||||
const auto low = NewUnary(state, OpBitcast, TypeU32(state), low_i);
|
||||
const auto high = Unary(state, OpBitcast, TypeU32(state), high_i);
|
||||
const auto low = Unary(state, OpBitcast, TypeU32(state), low_i);
|
||||
const auto high_nonzero =
|
||||
NewBinary(state, OpINotEqual, TypeBool(state), pair.high, ConstantU32(state, 0));
|
||||
return NewSelect(state, TypeU32(state), high_nonzero,
|
||||
NewBinary(state, OpIAdd, TypeU32(state), high, ConstantU32(state, 32)), low);
|
||||
Binary(state, OpINotEqual, TypeBool(state), pair.high, ConstantU32(state, 0));
|
||||
return Select(state, TypeU32(state), high_nonzero,
|
||||
Binary(state, OpIAdd, TypeU32(state), high, ConstantU32(state, 32)), low);
|
||||
}
|
||||
|
||||
uint32_t EmitMinMax3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bool signed_value,
|
||||
@@ -226,10 +206,10 @@ uint32_t EmitMinMax3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bo
|
||||
uint32_t EmitMed3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bool signed_value) {
|
||||
const auto minimum = EmitMinMax3(state, a, b, c, signed_value, false);
|
||||
const auto maximum = EmitMinMax3(state, a, b, c, signed_value, true);
|
||||
const auto ab = NewBinary(state, OpIAdd, TypeU32(state), a, b);
|
||||
const auto abc = NewBinary(state, OpIAdd, TypeU32(state), ab, c);
|
||||
return NewBinary(state, OpISub, TypeU32(state),
|
||||
NewBinary(state, OpISub, TypeU32(state), abc, minimum), maximum);
|
||||
const auto ab = Binary(state, OpIAdd, TypeU32(state), a, b);
|
||||
const auto abc = Binary(state, OpIAdd, TypeU32(state), ab, c);
|
||||
return Binary(state, OpISub, TypeU32(state),
|
||||
Binary(state, OpISub, TypeU32(state), abc, minimum), maximum);
|
||||
}
|
||||
|
||||
uint32_t EmitFMinMax3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bool max_value) {
|
||||
@@ -242,11 +222,11 @@ uint32_t EmitFMed3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c) {
|
||||
const auto max_ab = EmitMinMaxF32Value(state, a, b, true);
|
||||
const auto high_min = EmitMinMaxF32Value(state, max_ab, c, false);
|
||||
const auto median = EmitMinMaxF32Value(state, min_ab, high_min, true);
|
||||
const auto nan_ab = NewBinary(state, OpLogicalOr, TypeBool(state),
|
||||
const auto nan_ab = Binary(state, OpLogicalOr, TypeBool(state),
|
||||
EmitClassifyF32(state, a).nan, EmitClassifyF32(state, b).nan);
|
||||
const auto any_nan =
|
||||
NewBinary(state, OpLogicalOr, TypeBool(state), nan_ab, EmitClassifyF32(state, c).nan);
|
||||
return NewSelect(state, TypeF32(state), any_nan, min3, median);
|
||||
Binary(state, OpLogicalOr, TypeBool(state), nan_ab, EmitClassifyF32(state, c).nan);
|
||||
return Select(state, TypeF32(state), any_nan, min3, median);
|
||||
}
|
||||
|
||||
uint32_t EmitExt(EmitterState& state, uint32_t type, uint32_t opcode,
|
||||
@@ -270,37 +250,30 @@ uint32_t EmitF32ToU32(EmitterState& state, uint32_t src, bool signed_value) {
|
||||
}
|
||||
const auto nan = EmitClassifyF32(state, src).nan;
|
||||
if (signed_value) {
|
||||
const auto below = NewBinary(state, OpFOrdLessThanEqual, TypeBool(state), src,
|
||||
const auto below = Binary(state, OpFOrdLessThanEqual, TypeBool(state), src,
|
||||
ConstantF32(state, 0xcf000000u));
|
||||
const auto above = NewBinary(state, OpFOrdGreaterThanEqual, TypeBool(state), src,
|
||||
const auto above = Binary(state, OpFOrdGreaterThanEqual, TypeBool(state), src,
|
||||
ConstantF32(state, 0x4f000000u));
|
||||
const auto high =
|
||||
NewSelect(state, TypeU32(state), above, ConstantU32(state, 0x7fffffffu), converted_raw);
|
||||
Select(state, TypeU32(state), above, ConstantU32(state, 0x7fffffffu), converted_raw);
|
||||
const auto low =
|
||||
NewSelect(state, TypeU32(state), below, ConstantU32(state, 0x80000000u), high);
|
||||
return NewSelect(state, TypeU32(state), nan, ConstantU32(state, 0), low);
|
||||
Select(state, TypeU32(state), below, ConstantU32(state, 0x80000000u), high);
|
||||
return Select(state, TypeU32(state), nan, ConstantU32(state, 0), low);
|
||||
}
|
||||
const auto below =
|
||||
NewBinary(state, OpFOrdLessThanEqual, TypeBool(state), src, ConstantF32(state, 0));
|
||||
const auto above = NewBinary(state, OpFOrdGreaterThanEqual, TypeBool(state), src,
|
||||
Binary(state, OpFOrdLessThanEqual, TypeBool(state), src, ConstantF32(state, 0));
|
||||
const auto above = Binary(state, OpFOrdGreaterThanEqual, TypeBool(state), src,
|
||||
ConstantF32(state, 0x4f800000u));
|
||||
const auto zero = NewBinary(state, OpLogicalOr, TypeBool(state), nan, below);
|
||||
const auto zero = Binary(state, OpLogicalOr, TypeBool(state), nan, below);
|
||||
const auto high =
|
||||
NewSelect(state, TypeU32(state), above, ConstantU32(state, 0xffffffffu), converted_raw);
|
||||
return NewSelect(state, TypeU32(state), zero, ConstantU32(state, 0), high);
|
||||
Select(state, TypeU32(state), above, ConstantU32(state, 0xffffffffu), converted_raw);
|
||||
return Select(state, TypeU32(state), zero, ConstantU32(state, 0), high);
|
||||
}
|
||||
|
||||
uint32_t EmitPackHalf(EmitterState& state, uint32_t src) {
|
||||
return EmitExt(state, TypeU32(state), GlslPackHalf2x16, {src});
|
||||
}
|
||||
|
||||
uint32_t EmitUnpackHalf(EmitterState& state, uint32_t bits) {
|
||||
const auto pair = EmitExt(state, TypeF32Vector(state, 2), GlslUnpackHalf2x16, {bits});
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeExtract, TypeF32(state), result, pair, 0});
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
@@ -341,7 +314,7 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
return true;
|
||||
}
|
||||
case IR::ValueOpcode::ConvertF32F16:
|
||||
ctx.Define(inst, EmitUnpackHalf(state, ctx.Arg(inst, 0)));
|
||||
ctx.Define(inst, EmitF16BitsToF32(state, ctx.Arg(inst, 0)));
|
||||
return true;
|
||||
case IR::ValueOpcode::ConvertS32F32:
|
||||
ctx.Define(inst, EmitF32ToU32(state, ctx.Arg(inst, 0), true));
|
||||
@@ -350,7 +323,7 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
ctx.Define(inst, EmitF32ToU32(state, ctx.Arg(inst, 0), false));
|
||||
return true;
|
||||
case IR::ValueOpcode::ConvertF32S32: {
|
||||
const auto signed_value = NewUnary(state, OpBitcast, TypeI32(state), ctx.Arg(inst, 0));
|
||||
const auto signed_value = Unary(state, OpBitcast, TypeI32(state), ctx.Arg(inst, 0));
|
||||
ctx.Emit(inst, OpConvertSToF, IR::Type::F32, {signed_value});
|
||||
return true;
|
||||
}
|
||||
@@ -394,9 +367,9 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
case IR::ValueOpcode::PackFloat2x16Rtz: {
|
||||
const auto low = EmitF32ToF16RtzBits(state, ctx.Arg(inst, 0));
|
||||
const auto high =
|
||||
NewBinary(state, OpShiftLeftLogical, TypeU32(state),
|
||||
Binary(state, OpShiftLeftLogical, TypeU32(state),
|
||||
EmitF32ToF16RtzBits(state, ctx.Arg(inst, 1)), ConstantU32(state, 16));
|
||||
ctx.Define(inst, NewBinary(state, OpBitwiseOr, TypeU32(state), low, high));
|
||||
ctx.Define(inst, Binary(state, OpBitwiseOr, TypeU32(state), low, high));
|
||||
return true;
|
||||
}
|
||||
case IR::ValueOpcode::FPAbs32:
|
||||
@@ -458,10 +431,10 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
return true;
|
||||
case IR::ValueOpcode::IAbs32: {
|
||||
const auto value = ctx.Arg(inst, 0);
|
||||
const auto neg = NewUnary(state, OpSNegate, TypeU32(state), value);
|
||||
const auto neg = Unary(state, OpSNegate, TypeU32(state), value);
|
||||
const auto negative =
|
||||
NewBinary(state, OpSLessThan, TypeBool(state), value, ConstantU32(state, 0));
|
||||
ctx.Define(inst, NewSelect(state, TypeU32(state), negative, neg, value));
|
||||
Binary(state, OpSLessThan, TypeBool(state), value, ConstantU32(state, 0));
|
||||
ctx.Define(inst, Select(state, TypeU32(state), negative, neg, value));
|
||||
return true;
|
||||
}
|
||||
case IR::ValueOpcode::ShiftLeftLogical32: return binary(OpShiftLeftLogical, IR::Type::U32);
|
||||
@@ -496,25 +469,25 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
case IR::ValueOpcode::BitwiseXor32: return binary(OpBitwiseXor, IR::Type::U32);
|
||||
case IR::ValueOpcode::BitwiseNot32: return unary(OpNot, IR::Type::U32);
|
||||
case IR::ValueOpcode::BitwiseAnd64:
|
||||
ctx.Define(inst, NewBinary(state, OpBitwiseAnd, TypeU64(state), ctx.Arg(inst, 0),
|
||||
ctx.Define(inst, Binary(state, OpBitwiseAnd, TypeU64(state), ctx.Arg(inst, 0),
|
||||
ctx.Arg(inst, 1)));
|
||||
return true;
|
||||
case IR::ValueOpcode::BitReverse32: return unary(OpBitReverse, IR::Type::U32);
|
||||
case IR::ValueOpcode::BitCount32: return unary(OpBitCount, IR::Type::U32);
|
||||
case IR::ValueOpcode::BitCount64: {
|
||||
const auto pair =
|
||||
ExtractPair(state, NewUnary(state, OpBitCount, TypeU64(state), ctx.Arg(inst, 0)));
|
||||
ctx.Define(inst, NewBinary(state, OpIAdd, TypeU32(state), pair.low, pair.high));
|
||||
ExtractPair(state, Unary(state, OpBitCount, TypeU64(state), ctx.Arg(inst, 0)));
|
||||
ctx.Define(inst, Binary(state, OpIAdd, TypeU32(state), pair.low, pair.high));
|
||||
return true;
|
||||
}
|
||||
case IR::ValueOpcode::FindILsb32: {
|
||||
const auto value = EmitExt(state, TypeI32(state), GlslFindILsb, {ctx.Arg(inst, 0)});
|
||||
ctx.Define(inst, NewUnary(state, OpBitcast, TypeU32(state), value));
|
||||
ctx.Define(inst, Unary(state, OpBitcast, TypeU32(state), value));
|
||||
return true;
|
||||
}
|
||||
case IR::ValueOpcode::FindUMsb32: {
|
||||
const auto value = EmitExt(state, TypeI32(state), GlslFindUMsb, {ctx.Arg(inst, 0)});
|
||||
ctx.Define(inst, NewUnary(state, OpBitcast, TypeU32(state), value));
|
||||
ctx.Define(inst, Unary(state, OpBitcast, TypeU32(state), value));
|
||||
return true;
|
||||
}
|
||||
case IR::ValueOpcode::FindUMsb64:
|
||||
@@ -638,13 +611,13 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
return true;
|
||||
case IR::ValueOpcode::FPRecip32: {
|
||||
const auto source = EmitFlushF32DenormToSignedZero(state, ctx.Arg(inst, 0));
|
||||
ctx.Define(inst, NewBinary(state, OpFDiv, TypeF32(state),
|
||||
ctx.Define(inst, Binary(state, OpFDiv, TypeF32(state),
|
||||
ConstantF32(state, 0x3f800000u), source));
|
||||
return true;
|
||||
}
|
||||
case IR::ValueOpcode::FPRecipIFlag32:
|
||||
// Integer-to-float inputs used by IFLAG cannot be denormal.
|
||||
ctx.Define(inst, NewBinary(state, OpFDiv, TypeF32(state),
|
||||
ctx.Define(inst, Binary(state, OpFDiv, TypeF32(state),
|
||||
ConstantF32(state, 0x3f800000u), ctx.Arg(inst, 0)));
|
||||
return true;
|
||||
case IR::ValueOpcode::FPRecipSqrt32:
|
||||
@@ -659,7 +632,7 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
case IR::ValueOpcode::FPCos: {
|
||||
auto source = EmitTrigCycleF32(state, ctx.Arg(inst, 0), op == IR::ValueOpcode::FPSin);
|
||||
source =
|
||||
NewBinary(state, OpFMul, TypeF32(state), source, ConstantF32(state, 0x40c90fdbu));
|
||||
Binary(state, OpFMul, TypeF32(state), source, ConstantF32(state, 0x40c90fdbu));
|
||||
ctx.Define(inst, EmitExt(state, TypeF32(state),
|
||||
op == IR::ValueOpcode::FPSin ? GlslSin : GlslCos, {source}));
|
||||
return true;
|
||||
@@ -673,7 +646,7 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
{EmitFlushF32DenormToSignedZero(state, ctx.Arg(inst, 0))}));
|
||||
return true;
|
||||
case IR::ValueOpcode::FPLdexp: {
|
||||
const auto exponent = NewUnary(state, OpBitcast, TypeI32(state), ctx.Arg(inst, 1));
|
||||
const auto exponent = Unary(state, OpBitcast, TypeI32(state), ctx.Arg(inst, 1));
|
||||
ctx.Define(inst,
|
||||
EmitExt(state, TypeF32(state), GlslLdexp, {ctx.Arg(inst, 0), exponent}));
|
||||
return true;
|
||||
|
||||
@@ -215,9 +215,9 @@ uint32_t EmitLogicalNotBool(EmitterState& state, uint32_t value) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
F32Class EmitClassifyF32(EmitterState& state, uint32_t value) {
|
||||
F32Class EmitClassifyF32Bits(EmitterState& state, uint32_t bits) {
|
||||
F32Class cls;
|
||||
cls.bits = EmitBitcastF32ToU32(state, value);
|
||||
cls.bits = bits;
|
||||
const auto abs_bits = EmitAndConstant(state, cls.bits, 0x7fffffffu);
|
||||
const auto exponent_bits = EmitAndConstant(state, abs_bits, 0x7f800000u);
|
||||
const auto mantissa_bits = EmitAndConstant(state, abs_bits, 0x007fffffu);
|
||||
@@ -228,6 +228,10 @@ F32Class EmitClassifyF32(EmitterState& state, uint32_t value) {
|
||||
return cls;
|
||||
}
|
||||
|
||||
F32Class EmitClassifyF32(EmitterState& state, uint32_t value) {
|
||||
return EmitClassifyF32Bits(state, EmitBitcastF32ToU32(state, value));
|
||||
}
|
||||
|
||||
uint32_t EmitClassMaskBitMatch(EmitterState& state, uint32_t mask, uint32_t bit,
|
||||
uint32_t class_match) {
|
||||
const auto selected =
|
||||
|
||||
@@ -6,13 +6,6 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
|
||||
uint32_t PixelParameterMappedLocation(const EmitterState& state, uint32_t attr) {
|
||||
if (state.stage != ShaderType::Pixel) {
|
||||
return attr;
|
||||
}
|
||||
return ShaderPixelParameterMappedLocation(*state.input_info.pixel, attr);
|
||||
}
|
||||
|
||||
uint32_t PixelParameterLocation(const EmitterState& state, uint32_t attr) {
|
||||
std::array<uint32_t, 32> active_inputs {};
|
||||
uint32_t active_count = 0;
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
|
||||
uint32_t EmitTrueBool(EmitterState& state) {
|
||||
return ConstantBool(state, true);
|
||||
}
|
||||
|
||||
DppTargetLane EmitDppQuadPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control) {
|
||||
const auto quad_base = state.builder.AllocateId();
|
||||
const auto lane = state.builder.AllocateId();
|
||||
@@ -23,7 +19,7 @@ DppTargetLane EmitDppQuadPermTargetLane(EmitterState& state, uint32_t subid, uin
|
||||
state.builder.AddFunction(
|
||||
{OpBitwiseAnd, TypeU32(state), selected, selected0, ConstantU32(state, 3)});
|
||||
state.builder.AddFunction({OpBitwiseOr, TypeU32(state), target, quad_base, selected});
|
||||
return {target, EmitTrueBool(state)};
|
||||
return {target, ConstantBool(state, true)};
|
||||
}
|
||||
|
||||
DppTargetLane EmitDppRowShiftTargetLane(EmitterState& state, uint32_t subid, uint32_t amount,
|
||||
@@ -70,7 +66,7 @@ DppTargetLane EmitDppRowRotateRightTargetLane(EmitterState& state, uint32_t subi
|
||||
{OpIAdd, TypeU32(state), plus, lane, ConstantU32(state, 16u - amount)});
|
||||
state.builder.AddFunction({OpSelect, TypeU32(state), selected, in_high, minus, plus});
|
||||
state.builder.AddFunction({OpBitwiseOr, TypeU32(state), target, row, selected});
|
||||
return {target, EmitTrueBool(state)};
|
||||
return {target, ConstantBool(state, true)};
|
||||
}
|
||||
|
||||
DppTargetLane EmitDppMirrorTargetLane(EmitterState& state, uint32_t subid, bool half_row) {
|
||||
@@ -87,7 +83,7 @@ DppTargetLane EmitDppMirrorTargetLane(EmitterState& state, uint32_t subid, bool
|
||||
state.builder.AddFunction(
|
||||
{OpISub, TypeU32(state), mirrored, ConstantU32(state, lane_mask), lane});
|
||||
state.builder.AddFunction({OpBitwiseOr, TypeU32(state), target, base, mirrored});
|
||||
return {target, EmitTrueBool(state)};
|
||||
return {target, ConstantBool(state, true)};
|
||||
}
|
||||
|
||||
DppTargetLane EmitDppTargetLane(EmitterState& state, uint32_t control) {
|
||||
@@ -114,9 +110,9 @@ DppTargetLane EmitDppTargetLane(EmitterState& state, uint32_t control) {
|
||||
const auto target = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpBitwiseXor, TypeU32(state), target, subid, ConstantU32(state, control & 0xfu)});
|
||||
return {target, EmitTrueBool(state)};
|
||||
return {target, ConstantBool(state, true)};
|
||||
}
|
||||
return {subid, EmitTrueBool(state)};
|
||||
return {subid, ConstantBool(state, true)};
|
||||
}
|
||||
|
||||
uint32_t EmitSubgroupLocalInvocationId(EmitterState& state) {
|
||||
@@ -184,7 +180,7 @@ uint32_t EmitVertexParameterComponentU32(EmitterState& state, const InputBinding
|
||||
if (count == 1u) {
|
||||
state.builder.AddFunction({OpLoad, scalar_type, raw, input.variable_id});
|
||||
} else {
|
||||
const auto pointer_type = VertexParameterScalarPointerType(state, kind);
|
||||
const auto pointer_type = TypePointer(state, StorageClassInput, scalar_type);
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpAccessChain, pointer_type, pointer, input.variable_id,
|
||||
ConstantU32(state, component)});
|
||||
@@ -203,14 +199,14 @@ uint32_t EmitVertexParameterComponentU32(EmitterState& state, const InputBinding
|
||||
uint32_t EmitSubgroupLaneActiveBool(EmitterState& state, uint32_t lane) {
|
||||
const auto active_ballot = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpGroupNonUniformBallot, TypeU32Vector(state, 4), active_ballot,
|
||||
ConstantU32(state, ScopeSubgroup), EmitTrueBool(state)});
|
||||
ConstantU32(state, ScopeSubgroup), ConstantBool(state, true)});
|
||||
return EmitBallotLaneActiveBool(state, active_ballot, lane);
|
||||
}
|
||||
uint32_t EmitBallotLaneActiveBool(EmitterState& state, uint32_t active_ballot, uint32_t lane) {
|
||||
const auto low = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), low, active_ballot, 0});
|
||||
uint32_t mask = low;
|
||||
if (state.wave_size == 64u) {
|
||||
if (state.program.wave_size == 64u) {
|
||||
const auto high = state.builder.AllocateId();
|
||||
const auto in_high = state.builder.AllocateId();
|
||||
const auto selected = state.builder.AllocateId();
|
||||
@@ -235,7 +231,7 @@ uint32_t EmitBallotLaneActiveBool(EmitterState& state, uint32_t active_ballot, u
|
||||
state.builder.AddFunction({OpBitwiseAnd, TypeU32(state), hit, mask, bit});
|
||||
state.builder.AddFunction({OpINotEqual, TypeBool(state), active, hit, ConstantU32(state, 0)});
|
||||
state.builder.AddFunction(
|
||||
{OpULessThan, TypeBool(state), in_range, lane, ConstantU32(state, state.wave_size)});
|
||||
{OpULessThan, TypeBool(state), in_range, lane, ConstantU32(state, state.program.wave_size)});
|
||||
state.builder.AddFunction({OpLogicalAnd, TypeBool(state), ret, active, in_range});
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -32,18 +32,6 @@ uint32_t ImageGatherComponent(uint32_t dmask) {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Unary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t value) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, value});
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t Binary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t lhs, uint32_t rhs) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, lhs, rhs});
|
||||
return result;
|
||||
}
|
||||
|
||||
bool HasFlag(const IR::MemoryInfo& mem, uint32_t flag) {
|
||||
return (mem.image_sample_flags & flag) != 0u;
|
||||
}
|
||||
@@ -768,8 +756,8 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
return true;
|
||||
}
|
||||
const auto* handle = image_arg.ResolveInstruction();
|
||||
const auto* source = image.source < ctx.program.descriptor_sources.size()
|
||||
? &ctx.program.descriptor_sources[image.source]
|
||||
const auto* source = image.source < state.program.descriptor_sources.size()
|
||||
? &state.program.descriptor_sources[image.source]
|
||||
: nullptr;
|
||||
if (handle == nullptr || source == nullptr || !source->indirect_image.has_value() ||
|
||||
source->indirect_image->key_arg >= handle->NumArgs()) {
|
||||
|
||||
@@ -360,7 +360,6 @@ struct EmitterState {
|
||||
ShaderStageInputInfo input_info;
|
||||
const IR::SpirvRequirements& requirements;
|
||||
ShaderType stage = ShaderType::Unknown;
|
||||
uint32_t wave_size = 64;
|
||||
uint32_t lane_count = 1;
|
||||
uint32_t lane_half = 0;
|
||||
uint32_t storage_buffer_variable = 0;
|
||||
@@ -386,6 +385,7 @@ struct EmitterState {
|
||||
uint32_t mesh_cull = 0;
|
||||
uint32_t entry_label = 0;
|
||||
uint32_t current_label = 0;
|
||||
const IR::Block* current_block = nullptr;
|
||||
uint32_t pixel_valid_mask_variable = 0;
|
||||
uint32_t subgroup_local_invocation_id_variable = 0;
|
||||
uint32_t per_vertex_variable = 0;
|
||||
@@ -401,6 +401,7 @@ struct EmitterState {
|
||||
std::vector<InputBinding> inputs;
|
||||
std::vector<OutputBinding> outputs;
|
||||
std::vector<uint32_t> interface_variables;
|
||||
std::unordered_map<const IR::Block*, uint32_t> labels;
|
||||
};
|
||||
|
||||
uint32_t TypeVoid(EmitterState& state);
|
||||
@@ -436,9 +437,28 @@ inline void EmitLabel(EmitterState& state, uint32_t label) {
|
||||
state.builder.AddFunction({OpLabel, label});
|
||||
}
|
||||
|
||||
inline uint32_t Unary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t value) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, value});
|
||||
return result;
|
||||
}
|
||||
|
||||
inline uint32_t Binary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t lhs,
|
||||
uint32_t rhs) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, lhs, rhs});
|
||||
return result;
|
||||
}
|
||||
|
||||
inline uint32_t Select(EmitterState& state, uint32_t type, uint32_t condition,
|
||||
uint32_t true_value, uint32_t false_value) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpSelect, type, result, condition, true_value, false_value});
|
||||
return result;
|
||||
}
|
||||
|
||||
struct ValueEmitContext {
|
||||
ValueEmitContext(EmitterState& state_, const IR::Program& program_)
|
||||
: state(state_), program(program_) {}
|
||||
explicit ValueEmitContext(EmitterState& state_): state(state_) {}
|
||||
|
||||
uint32_t Def(IR::Value value);
|
||||
uint32_t Arg(const IR::Inst& inst, size_t index);
|
||||
@@ -460,12 +480,9 @@ struct ValueEmitContext {
|
||||
[[noreturn]] void Fail(const IR::Inst& inst, const char* reason) const;
|
||||
|
||||
EmitterState& state;
|
||||
const IR::Program& program;
|
||||
std::unordered_map<const IR::Inst*, uint32_t> definitions;
|
||||
std::unordered_map<const IR::Block*, uint32_t> labels;
|
||||
const std::unordered_map<const IR::Inst*, uint32_t>* dispatcher_spills = nullptr;
|
||||
std::unordered_map<const IR::Inst*, std::pair<uint32_t, uint32_t>> dispatcher_block_loads;
|
||||
const IR::Block* current_block = nullptr;
|
||||
uint32_t scratch_u32_variable = 0;
|
||||
ValueEmitContext* other_half = nullptr;
|
||||
uint32_t half = 0;
|
||||
@@ -496,8 +513,6 @@ struct F32Class {
|
||||
uint32_t zero = 0;
|
||||
};
|
||||
|
||||
uint32_t PixelParameterMappedLocation(const EmitterState& state, uint32_t attr);
|
||||
|
||||
uint32_t PixelParameterLocation(const EmitterState& state, uint32_t attr);
|
||||
|
||||
bool PixelParameterIsFlat(const EmitterState& state, uint32_t attr);
|
||||
@@ -510,11 +525,6 @@ uint32_t VertexParameterComponentCount(const InputBinding& input);
|
||||
|
||||
uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind kind);
|
||||
|
||||
uint32_t VertexParameterScalarPointerType(EmitterState& state, VertexInputScalarKind kind);
|
||||
|
||||
uint32_t VertexParameterVectorOrScalarType(EmitterState& state, VertexInputScalarKind kind,
|
||||
uint32_t components);
|
||||
|
||||
uint32_t VertexParameterInputPointerType(EmitterState& state, VertexInputScalarKind kind,
|
||||
uint32_t components);
|
||||
|
||||
@@ -602,8 +612,6 @@ void EmitMeshAllocate(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||
uint32_t MeshOutputPointer(EmitterState& state, IR::StageOutputKind kind, uint32_t index = 0);
|
||||
uint32_t MeshPrimitivePointer(EmitterState& state);
|
||||
|
||||
uint32_t EmitTrueBool(EmitterState& state);
|
||||
|
||||
DppTargetLane EmitDppQuadPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control);
|
||||
|
||||
DppTargetLane EmitDppRowShiftTargetLane(EmitterState& state, uint32_t subid, uint32_t amount,
|
||||
@@ -675,22 +683,13 @@ uint32_t EmitStorageBufferElementPointer(EmitterState& state,
|
||||
const MemoryResourceAccess& access, uint32_t index,
|
||||
uint32_t pointer_type);
|
||||
|
||||
uint32_t EmitTBufferBitcastF32ToU32(EmitterState& state, uint32_t value);
|
||||
|
||||
uint32_t EmitTBufferBitcastU32ToF32(EmitterState& state, uint32_t value);
|
||||
|
||||
uint32_t EmitTBufferBitcastU32ToI32(EmitterState& state, uint32_t value);
|
||||
|
||||
uint32_t EmitTBufferCompareU32Constant(EmitterState& state, uint32_t opcode, uint32_t value,
|
||||
uint32_t constant);
|
||||
|
||||
uint32_t EmitTBufferSelectF32(EmitterState& state, uint32_t condition, uint32_t true_value,
|
||||
uint32_t false_value);
|
||||
|
||||
bool IsSignedFormatComponent(Format::ComponentType type);
|
||||
|
||||
uint32_t EmitHalfToF32Bits(EmitterState& state, uint32_t raw);
|
||||
|
||||
uint32_t EmitUFloatToF32Bits(EmitterState& state, uint32_t raw, uint32_t bits);
|
||||
|
||||
uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferFormatInfo& info,
|
||||
@@ -741,6 +740,8 @@ uint32_t EmitLogicalOrBool(EmitterState& state, uint32_t lhs, uint32_t rhs);
|
||||
|
||||
uint32_t EmitLogicalNotBool(EmitterState& state, uint32_t value);
|
||||
|
||||
F32Class EmitClassifyF32Bits(EmitterState& state, uint32_t bits);
|
||||
|
||||
F32Class EmitClassifyF32(EmitterState& state, uint32_t value);
|
||||
|
||||
uint32_t EmitClassMaskBitMatch(EmitterState& state, uint32_t mask, uint32_t bit,
|
||||
@@ -770,7 +771,7 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||
|
||||
bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||
|
||||
void EmitProgram(EmitterState& state, const IR::Program& program);
|
||||
void EmitProgram(EmitterState& state);
|
||||
|
||||
void DefineGetBdaPointer(EmitterState& state);
|
||||
|
||||
|
||||
@@ -7,25 +7,6 @@
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
namespace {
|
||||
|
||||
uint32_t Unary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t value) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, value});
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t Binary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t lhs, uint32_t rhs) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, lhs, rhs});
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t Select(EmitterState& state, uint32_t type, uint32_t condition, uint32_t true_value,
|
||||
uint32_t false_value) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpSelect, type, result, condition, true_value, false_value});
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t AndCondition(EmitterState& state, uint32_t lhs, uint32_t rhs) {
|
||||
return Binary(state, OpLogicalAnd, TypeBool(state), lhs, rhs);
|
||||
}
|
||||
@@ -290,12 +271,6 @@ PreparedMemoryElement PrepareMemoryElement(ValueEmitContext& ctx, const IR::Memo
|
||||
return {.resource = resource, .index = index};
|
||||
}
|
||||
|
||||
PreparedMemoryElement PrepareMemoryElement(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
||||
const MemoryResourceAccess& resource,
|
||||
uint32_t raw_index) {
|
||||
return {.resource = resource, .index = EmitMemoryElementIndex(ctx.state, resource, raw_index)};
|
||||
}
|
||||
|
||||
uint32_t LoadWordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess& resource,
|
||||
uint32_t index);
|
||||
|
||||
@@ -304,10 +279,10 @@ uint32_t LoadSubwordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess&
|
||||
|
||||
uint32_t LoadWordPrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
|
||||
const MemoryResourceAccess& resource) {
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, DwordIndex(ctx, inst, mem));
|
||||
const auto index = EmitMemoryElementIndex(ctx.state, resource, DwordIndex(ctx, inst, mem));
|
||||
return EmitValueOrZeroIfCondition(
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index),
|
||||
[&]() { return LoadWordInBounds(ctx, access.resource, access.index); });
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, resource, index),
|
||||
[&]() { return LoadWordInBounds(ctx, resource, index); });
|
||||
}
|
||||
|
||||
uint32_t LoadWord(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo mem) {
|
||||
@@ -323,11 +298,10 @@ uint32_t LoadSubwordPrepared(ValueEmitContext& ctx, const IR::Inst& inst, const
|
||||
const auto address = ByteAddress(ctx, inst, mem);
|
||||
const auto raw_index = Binary(ctx.state, OpShiftRightLogical, TypeU32(ctx.state), address,
|
||||
ConstantU32(ctx.state, 2));
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, raw_index);
|
||||
const auto index = EmitMemoryElementIndex(ctx.state, resource, raw_index);
|
||||
return EmitValueOrZeroIfCondition(
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index), [&]() {
|
||||
return LoadSubwordInBounds(ctx, access.resource, address, access.index, bits,
|
||||
sign_extend);
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, resource, index), [&]() {
|
||||
return LoadSubwordInBounds(ctx, resource, address, index, bits, sign_extend);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -365,15 +339,14 @@ uint32_t LoadSubword(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo
|
||||
});
|
||||
}
|
||||
|
||||
Prospero::BufferFormat BufferFormat(const ValueEmitContext& ctx, const IR::Inst& inst,
|
||||
const IR::MemoryInfo& mem) {
|
||||
Prospero::BufferFormat BufferFormat(const ValueEmitContext& ctx, const IR::MemoryInfo& mem) {
|
||||
return mem.typed ? Format::DecodeTBufferFormat(mem.data_format, mem.number_format)
|
||||
: StorageBufferFormat(ctx.state, mem);
|
||||
}
|
||||
|
||||
IR::MemoryInfo RebaseFormattedComponent(IR::MemoryInfo mem, Prospero::BufferFormat format,
|
||||
IR::MemoryInfo RebaseFormattedComponent(IR::MemoryInfo mem, const Format::BufferFormatInfo& info,
|
||||
uint32_t component) {
|
||||
mem.offset += Format::GetFormatComponentByteOffset(format, component);
|
||||
mem.offset += Format::GetFormatComponentByteOffset(info, component);
|
||||
mem.data_dwords = 1u;
|
||||
mem.component_index = component;
|
||||
return mem;
|
||||
@@ -414,7 +387,7 @@ uint32_t FormattedConstant(ValueEmitContext& ctx, const Format::BufferFormatInfo
|
||||
|
||||
template <typename LoadWordFn, typename LoadSubwordFn>
|
||||
uint32_t LoadFormattedComponent(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
||||
Prospero::BufferFormat format, const Format::BufferFormatInfo& info,
|
||||
const Format::BufferFormatInfo& info,
|
||||
uint32_t output_component, LoadWordFn&& load_word,
|
||||
LoadSubwordFn&& load_subword) {
|
||||
const auto source = ResolveFormattedSource(ctx, mem, info, output_component);
|
||||
@@ -449,19 +422,18 @@ uint32_t LoadFormattedComponent(ValueEmitContext& ctx, const IR::MemoryInfo& mem
|
||||
uint32_t FormattedLoadPrepared(ValueEmitContext& ctx, const IR::Inst& inst,
|
||||
const IR::MemoryInfo& mem, uint32_t output_component,
|
||||
const MemoryResourceAccess& resource) {
|
||||
const auto format = BufferFormat(ctx, inst, mem);
|
||||
if (!Format::IsKnownFormat(format)) {
|
||||
const auto info = Format::GetFormatInfo(BufferFormat(ctx, mem));
|
||||
if (info.type == Format::ComponentType::Unknown) {
|
||||
return LoadWordPrepared(ctx, inst, RebaseRawComponent(mem, output_component), resource);
|
||||
}
|
||||
const auto info = Format::GetFormatInfo(format);
|
||||
return LoadFormattedComponent(
|
||||
ctx, mem, format, info, output_component,
|
||||
ctx, mem, info, output_component,
|
||||
[&](uint32_t component) {
|
||||
return LoadWordPrepared(ctx, inst, RebaseFormattedComponent(mem, format, component),
|
||||
return LoadWordPrepared(ctx, inst, RebaseFormattedComponent(mem, info, component),
|
||||
resource);
|
||||
},
|
||||
[&](uint32_t component, uint32_t bits, bool sign_extend) {
|
||||
return LoadSubwordPrepared(ctx, inst, RebaseFormattedComponent(mem, format, component),
|
||||
return LoadSubwordPrepared(ctx, inst, RebaseFormattedComponent(mem, info, component),
|
||||
resource, bits, sign_extend);
|
||||
});
|
||||
}
|
||||
@@ -507,10 +479,10 @@ void StoreSubwordPrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR:
|
||||
const auto address = ByteAddress(ctx, inst, mem);
|
||||
const auto raw_index = Binary(ctx.state, OpShiftRightLogical, TypeU32(ctx.state), address,
|
||||
ConstantU32(ctx.state, 2));
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, raw_index);
|
||||
const auto index = EmitMemoryElementIndex(ctx.state, resource, raw_index);
|
||||
EmitIfCondition(
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index), [&]() {
|
||||
StoreSubwordInBounds(ctx, mem, access.resource, address, access.index, bits, data);
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, resource, index), [&]() {
|
||||
StoreSubwordInBounds(ctx, mem, resource, address, index, bits, data);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -523,12 +495,11 @@ void StoreSubword(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo me
|
||||
|
||||
void StoreWordPrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
|
||||
const MemoryResourceAccess& resource, uint32_t data) {
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, DwordIndex(ctx, inst, mem));
|
||||
const auto index = EmitMemoryElementIndex(ctx.state, resource, DwordIndex(ctx, inst, mem));
|
||||
EmitIfCondition(
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index), [&]() {
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, resource, index), [&]() {
|
||||
ctx.state.builder.AddFunction(
|
||||
{OpStore, EmitMemoryElementPointer(ctx.state, access.resource, access.index),
|
||||
data});
|
||||
{OpStore, EmitMemoryElementPointer(ctx.state, resource, index), data});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -548,15 +519,14 @@ void StoreWord(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo mem)
|
||||
void FormattedStorePrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
|
||||
uint32_t component, const MemoryResourceAccess& resource,
|
||||
uint32_t data) {
|
||||
const auto format = BufferFormat(ctx, inst, mem);
|
||||
if (!Format::IsKnownFormat(format)) {
|
||||
const auto info = Format::GetFormatInfo(BufferFormat(ctx, mem));
|
||||
if (info.type == Format::ComponentType::Unknown) {
|
||||
StoreWordPrepared(ctx, inst, RebaseRawComponent(mem, component), resource, data);
|
||||
return;
|
||||
}
|
||||
const auto info = Format::GetFormatInfo(format);
|
||||
if (component >= info.component_count) return;
|
||||
const auto bits = info.component_bits[component];
|
||||
const auto component_mem = RebaseFormattedComponent(mem, format, component);
|
||||
const auto component_mem = RebaseFormattedComponent(mem, info, component);
|
||||
if (bits == 8u || bits == 16u) {
|
||||
StoreSubwordPrepared(ctx, inst, component_mem, resource, bits, data);
|
||||
} else {
|
||||
@@ -781,7 +751,6 @@ uint32_t AppendConsume(ValueEmitContext& ctx, const IR::Inst& inst, bool append)
|
||||
}
|
||||
|
||||
struct PreparedFormattedMemory {
|
||||
Prospero::BufferFormat format = Prospero::BufferFormat::kInvalid;
|
||||
Format::BufferFormatInfo info;
|
||||
MemoryResourceAccess resource;
|
||||
std::array<uint32_t, 4> addresses {};
|
||||
@@ -794,10 +763,10 @@ enum class FormattedAccess { Load, Store };
|
||||
PreparedFormattedMemory PrepareFormattedMemory(ValueEmitContext& ctx, const IR::Inst& inst,
|
||||
const IR::MemoryInfo& mem,
|
||||
const MemoryResourceAccess& resource,
|
||||
const Format::BufferFormatInfo& info,
|
||||
uint32_t components, FormattedAccess access) {
|
||||
PreparedFormattedMemory plan;
|
||||
plan.format = BufferFormat(ctx, inst, mem);
|
||||
plan.info = Format::GetFormatInfo(plan.format);
|
||||
plan.info = info;
|
||||
plan.resource = resource;
|
||||
std::array<bool, 4> required_components {};
|
||||
if (access == FormattedAccess::Load) {
|
||||
@@ -816,11 +785,11 @@ PreparedFormattedMemory PrepareFormattedMemory(ValueEmitContext& ctx, const IR::
|
||||
bool first_bound = true;
|
||||
for (uint32_t component = 0; component < plan.info.component_count; component++) {
|
||||
if (!required_components[component]) continue;
|
||||
const auto byte_offset = Format::GetFormatComponentByteOffset(plan.format, component);
|
||||
const auto byte_offset = Format::GetFormatComponentByteOffset(plan.info, component);
|
||||
bool reused = false;
|
||||
for (uint32_t previous = 0; previous < component; previous++) {
|
||||
if (required_components[previous] &&
|
||||
Format::GetFormatComponentByteOffset(plan.format, previous) == byte_offset) {
|
||||
Format::GetFormatComponentByteOffset(plan.info, previous) == byte_offset) {
|
||||
plan.addresses[component] = plan.addresses[previous];
|
||||
plan.indices[component] = plan.indices[previous];
|
||||
reused = true;
|
||||
@@ -828,7 +797,7 @@ PreparedFormattedMemory PrepareFormattedMemory(ValueEmitContext& ctx, const IR::
|
||||
}
|
||||
}
|
||||
if (reused) continue;
|
||||
const auto component_mem = RebaseFormattedComponent(mem, plan.format, component);
|
||||
const auto component_mem = RebaseFormattedComponent(mem, plan.info, component);
|
||||
plan.addresses[component] = ByteAddress(ctx, inst, component_mem);
|
||||
const auto raw_index = Binary(ctx.state, OpShiftRightLogical, TypeU32(ctx.state),
|
||||
plan.addresses[component], ConstantU32(ctx.state, 2));
|
||||
@@ -849,7 +818,7 @@ PreparedFormattedMemory PrepareFormattedMemory(ValueEmitContext& ctx, const IR::
|
||||
uint32_t LoadFormattedInBounds(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
||||
const PreparedFormattedMemory& plan, uint32_t output_component) {
|
||||
return LoadFormattedComponent(
|
||||
ctx, mem, plan.format, plan.info, output_component,
|
||||
ctx, mem, plan.info, output_component,
|
||||
[&](uint32_t component) {
|
||||
return LoadWordInBounds(ctx, plan.resource, plan.indices[component]);
|
||||
},
|
||||
@@ -898,10 +867,10 @@ uint32_t LoadWideBuffer(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t co
|
||||
ConstantU32CompositeZero(state, components), [&]() {
|
||||
const auto mem = ctx.Memory(inst);
|
||||
const auto resource = PrepareMemoryResourceAccess(state, mem);
|
||||
const auto format =
|
||||
mem.formatted ? BufferFormat(ctx, inst, mem) : Prospero::BufferFormat::kInvalid;
|
||||
if (Format::IsKnownFormat(format)) {
|
||||
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, components,
|
||||
const auto info = Format::GetFormatInfo(
|
||||
mem.formatted ? BufferFormat(ctx, mem) : Prospero::BufferFormat::kInvalid);
|
||||
if (info.type != Format::ComponentType::Unknown) {
|
||||
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info, components,
|
||||
FormattedAccess::Load);
|
||||
return EmitValueOrDefaultIfCondition(
|
||||
state, plan.in_bounds, TypeU32Composite(state, components),
|
||||
@@ -928,10 +897,10 @@ void StoreWideBuffer(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t compo
|
||||
const auto mem = ctx.Memory(inst);
|
||||
const auto resource = PrepareMemoryResourceAccess(state, mem);
|
||||
const auto composite = ctx.Arg(inst, inst.NumArgs() - 2);
|
||||
const auto format =
|
||||
mem.formatted ? BufferFormat(ctx, inst, mem) : Prospero::BufferFormat::kInvalid;
|
||||
if (Format::IsKnownFormat(format)) {
|
||||
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, components,
|
||||
const auto info = Format::GetFormatInfo(
|
||||
mem.formatted ? BufferFormat(ctx, mem) : Prospero::BufferFormat::kInvalid);
|
||||
if (info.type != Format::ComponentType::Unknown) {
|
||||
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info, components,
|
||||
FormattedAccess::Store);
|
||||
EmitIfCondition(state, plan.in_bounds, [&]() {
|
||||
for (uint32_t component = 0; component < components; component++) {
|
||||
@@ -967,10 +936,10 @@ uint32_t LoadWideShared(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t co
|
||||
ConstantU32(state, component * 4u));
|
||||
const auto raw_index = Binary(state, OpShiftRightLogical, TypeU32(state), address,
|
||||
ConstantU32(state, 2));
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, raw_index);
|
||||
const auto index = EmitMemoryElementIndex(state, resource, raw_index);
|
||||
values[component] = EmitValueOrZeroIfCondition(
|
||||
state, EmitMemoryElementInBounds(state, access.resource, access.index),
|
||||
[&]() { return LoadWordInBounds(ctx, access.resource, access.index); });
|
||||
state, EmitMemoryElementInBounds(state, resource, index),
|
||||
[&]() { return LoadWordInBounds(ctx, resource, index); });
|
||||
}
|
||||
return ConstructU32Composite(state, components, values);
|
||||
});
|
||||
@@ -988,11 +957,10 @@ void StoreWideShared(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t compo
|
||||
ConstantU32(state, component * 4u));
|
||||
const auto raw_index =
|
||||
Binary(state, OpShiftRightLogical, TypeU32(state), address, ConstantU32(state, 2));
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, raw_index);
|
||||
EmitIfCondition(state, EmitMemoryElementInBounds(state, access.resource, access.index),
|
||||
const auto index = EmitMemoryElementIndex(state, resource, raw_index);
|
||||
EmitIfCondition(state, EmitMemoryElementInBounds(state, resource, index),
|
||||
[&]() {
|
||||
StoreWordInBounds(ctx, access.resource, access.index,
|
||||
ctx.Arg(inst, component + 1u));
|
||||
StoreWordInBounds(ctx, resource, index, ctx.Arg(inst, component + 1u));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -188,31 +188,12 @@ uint32_t EmitStorageBufferElementPointer(EmitterState& state,
|
||||
return pointer;
|
||||
}
|
||||
|
||||
uint32_t EmitTBufferBitcastF32ToU32(EmitterState& state, uint32_t value) {
|
||||
const auto ret = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitcast, TypeU32(state), ret, value});
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t EmitTBufferBitcastU32ToF32(EmitterState& state, uint32_t value) {
|
||||
const auto ret = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitcast, TypeF32(state), ret, value});
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t EmitTBufferBitcastU32ToI32(EmitterState& state, uint32_t value) {
|
||||
const auto ret = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitcast, TypeI32(state), ret, value});
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t EmitTBufferCompareU32Constant(EmitterState& state, uint32_t opcode, uint32_t value,
|
||||
uint32_t constant) {
|
||||
const auto ret = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, TypeBool(state), ret, value, ConstantU32(state, constant)});
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t EmitTBufferSelectF32(EmitterState& state, uint32_t condition, uint32_t true_value,
|
||||
uint32_t false_value) {
|
||||
const auto ret = state.builder.AllocateId();
|
||||
@@ -225,15 +206,6 @@ bool IsSignedFormatComponent(Format::ComponentType type) {
|
||||
type == Format::ComponentType::Sscaled;
|
||||
}
|
||||
|
||||
uint32_t EmitHalfToF32Bits(EmitterState& state, uint32_t raw) {
|
||||
const auto unpacked = state.builder.AllocateId();
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpExtInst, TypeF32Vector(state, 2), unpacked, GlslStd450(state), GlslUnpackHalf2x16, raw});
|
||||
state.builder.AddFunction({OpCompositeExtract, TypeF32(state), value, unpacked, 0});
|
||||
return EmitTBufferBitcastF32ToU32(state, value);
|
||||
}
|
||||
|
||||
uint32_t EmitUFloatToF32Bits(EmitterState& state, uint32_t raw, uint32_t bits) {
|
||||
const auto mantissa_bits = bits == 11u ? 6u : 5u;
|
||||
const auto mantissa_mask = (1u << mantissa_bits) - 1u;
|
||||
@@ -250,11 +222,11 @@ uint32_t EmitUFloatToF32Bits(EmitterState& state, uint32_t raw, uint32_t bits) {
|
||||
const auto mantissa_bits_32 =
|
||||
EmitBinaryU32(state, OpShiftLeftLogical, mantissa, ConstantU32(state, 23u - mantissa_bits));
|
||||
const auto normal_bits = EmitBinaryU32(state, OpBitwiseOr, exponent_bits, mantissa_bits_32);
|
||||
const auto normal = EmitTBufferBitcastU32ToF32(state, normal_bits);
|
||||
const auto normal = EmitBitcastU32ToF32(state, normal_bits);
|
||||
|
||||
const auto special_bits =
|
||||
EmitBinaryU32(state, OpBitwiseOr, ConstantU32(state, 0x7f800000u), mantissa_bits_32);
|
||||
const auto special = EmitTBufferBitcastU32ToF32(state, special_bits);
|
||||
const auto special = EmitBitcastU32ToF32(state, special_bits);
|
||||
|
||||
const auto mantissa_f32 = state.builder.AllocateId();
|
||||
const auto subnormal = state.builder.AllocateId();
|
||||
@@ -263,11 +235,11 @@ uint32_t EmitUFloatToF32Bits(EmitterState& state, uint32_t raw, uint32_t bits) {
|
||||
{OpFMul, TypeF32(state), subnormal, mantissa_f32,
|
||||
ConstantF32Value(state, std::ldexp(1.0f, 1 - 15 - static_cast<int>(mantissa_bits)))});
|
||||
|
||||
const auto zero_exp = EmitTBufferCompareU32Constant(state, OpIEqual, exponent, 0);
|
||||
const auto special_exp = EmitTBufferCompareU32Constant(state, OpIEqual, exponent, 31);
|
||||
const auto zero_exp = EmitCompareU32Constant(state, OpIEqual, exponent, 0);
|
||||
const auto special_exp = EmitCompareU32Constant(state, OpIEqual, exponent, 31);
|
||||
const auto finite = EmitTBufferSelectF32(state, zero_exp, subnormal, normal);
|
||||
const auto result = EmitTBufferSelectF32(state, special_exp, special, finite);
|
||||
return EmitTBufferBitcastF32ToU32(state, result);
|
||||
return EmitBitcastF32ToU32(state, result);
|
||||
}
|
||||
|
||||
uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferFormatInfo& info,
|
||||
@@ -279,13 +251,13 @@ uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferForma
|
||||
case Format::ComponentType::Uscaled: {
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpConvertUToF, TypeF32(state), value, raw});
|
||||
return EmitTBufferBitcastF32ToU32(state, value);
|
||||
return EmitBitcastF32ToU32(state, value);
|
||||
}
|
||||
case Format::ComponentType::Sscaled: {
|
||||
const auto signed_raw = EmitTBufferBitcastU32ToI32(state, raw);
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpConvertSToF, TypeF32(state), value, signed_raw});
|
||||
return EmitTBufferBitcastF32ToU32(state, value);
|
||||
return EmitBitcastF32ToU32(state, value);
|
||||
}
|
||||
case Format::ComponentType::Unorm: {
|
||||
const auto value = state.builder.AllocateId();
|
||||
@@ -294,7 +266,7 @@ uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferForma
|
||||
state.builder.AddFunction({OpConvertUToF, TypeF32(state), value, raw});
|
||||
state.builder.AddFunction(
|
||||
{OpFDiv, TypeF32(state), normalized, value, ConstantF32Value(state, max_value)});
|
||||
return EmitTBufferBitcastF32ToU32(state, normalized);
|
||||
return EmitBitcastF32ToU32(state, normalized);
|
||||
}
|
||||
case Format::ComponentType::Snorm: {
|
||||
const auto signed_raw = EmitTBufferBitcastU32ToI32(state, raw);
|
||||
@@ -307,14 +279,14 @@ uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferForma
|
||||
{OpFDiv, TypeF32(state), normalized, value, ConstantF32Value(state, max_value)});
|
||||
state.builder.AddFunction({OpExtInst, TypeF32(state), clamped, GlslStd450(state),
|
||||
GlslFMax, normalized, ConstantF32Value(state, -1.0f)});
|
||||
return EmitTBufferBitcastF32ToU32(state, clamped);
|
||||
return EmitBitcastF32ToU32(state, clamped);
|
||||
}
|
||||
case Format::ComponentType::Float:
|
||||
if (bits == 32u) {
|
||||
return raw;
|
||||
}
|
||||
if (bits == 16u) {
|
||||
return EmitHalfToF32Bits(state, raw);
|
||||
return EmitBitcastF32ToU32(state, EmitF16BitsToF32(state, raw));
|
||||
}
|
||||
return EmitUFloatToF32Bits(state, raw, bits);
|
||||
default: return raw;
|
||||
@@ -335,8 +307,7 @@ uint32_t EmitFloatAtomicReplacement(EmitterState& state, uint32_t old, uint32_t
|
||||
uint32_t key;
|
||||
};
|
||||
const auto classify = [&](uint32_t bits) {
|
||||
const auto value = EmitBitcastU32ToF32(state, bits);
|
||||
const auto cls = EmitClassifyF32(state, value);
|
||||
const auto cls = EmitClassifyF32Bits(state, bits);
|
||||
const auto negative = EmitCompareU32Constant(
|
||||
state, OpINotEqual, EmitAndConstant(state, bits, 0x80000000u), 0u);
|
||||
const auto negative_key = state.builder.AllocateId();
|
||||
|
||||
@@ -349,45 +349,6 @@ uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind ki
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t VertexParameterScalarPointerType(EmitterState& state, VertexInputScalarKind kind) {
|
||||
switch (kind) {
|
||||
case VertexInputScalarKind::Sint:
|
||||
return TypePointer(state, StorageClassInput, TypeI32(state));
|
||||
case VertexInputScalarKind::Uint:
|
||||
return TypePointer(state, StorageClassInput, TypeU32(state));
|
||||
case VertexInputScalarKind::Float:
|
||||
default: return TypePointer(state, StorageClassInput, TypeF32(state));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t VertexParameterVectorOrScalarType(EmitterState& state, VertexInputScalarKind kind,
|
||||
uint32_t components) {
|
||||
switch (kind) {
|
||||
case VertexInputScalarKind::Sint:
|
||||
switch (components) {
|
||||
case 1: return TypeI32(state);
|
||||
case 2: return TypeI32Vector(state, 2);
|
||||
case 3: return TypeI32Vector(state, 3);
|
||||
default: return TypeI32Vector(state, 4);
|
||||
}
|
||||
case VertexInputScalarKind::Uint:
|
||||
switch (components) {
|
||||
case 1: return TypeU32(state);
|
||||
case 2: return TypeU32Vector(state, 2);
|
||||
case 3: return TypeU32Vector(state, 3);
|
||||
default: return TypeU32Vector(state, 4);
|
||||
}
|
||||
case VertexInputScalarKind::Float:
|
||||
default:
|
||||
switch (components) {
|
||||
case 1: return TypeF32(state);
|
||||
case 2: return TypeF32Vector(state, 2);
|
||||
case 3: return TypeF32Vector(state, 3);
|
||||
default: return TypeF32Vector(state, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t VertexParameterInputPointerType(EmitterState& state, VertexInputScalarKind kind,
|
||||
uint32_t components) {
|
||||
switch (kind) {
|
||||
|
||||
@@ -118,18 +118,19 @@ uint32_t BranchCondition(ValueEmitContext& ctx, const IR::BlockInfo& info) {
|
||||
|
||||
void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
|
||||
const IR::BlockInfo& info) {
|
||||
const auto& program = ctx.state.program;
|
||||
const auto& term = info.terminator;
|
||||
const auto emit_merge = [&]() {
|
||||
if (term.loop_header) {
|
||||
const auto* merge = TargetBlock(ctx.program, term.merge_block);
|
||||
const auto* cont = TargetBlock(ctx.program, term.continue_block);
|
||||
const auto* merge = TargetBlock(program, term.merge_block);
|
||||
const auto* cont = TargetBlock(program, term.continue_block);
|
||||
if (merge != nullptr && cont != nullptr) {
|
||||
ctx.state.builder.AddFunction(
|
||||
{OpLoopMerge, ctx.Label(merge), ctx.Label(cont), LoopControlNone});
|
||||
}
|
||||
} else if (term.kind == CFG::TerminatorKind::ConditionalBranch &&
|
||||
term.merge_block != UINT32_MAX) {
|
||||
if (const auto* merge = TargetBlock(ctx.program, term.merge_block); merge != nullptr) {
|
||||
if (const auto* merge = TargetBlock(program, term.merge_block); merge != nullptr) {
|
||||
ctx.state.builder.AddFunction(
|
||||
{OpSelectionMerge, ctx.Label(merge), SelectionControlNone});
|
||||
}
|
||||
@@ -138,7 +139,7 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
|
||||
|
||||
switch (term.kind) {
|
||||
case CFG::TerminatorKind::Branch: {
|
||||
const auto* target = TargetBlock(ctx.program, term.true_block);
|
||||
const auto* target = TargetBlock(program, term.true_block);
|
||||
if (target == nullptr) {
|
||||
EmitReturn(ctx);
|
||||
return;
|
||||
@@ -148,8 +149,8 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
|
||||
return;
|
||||
}
|
||||
case CFG::TerminatorKind::ConditionalBranch: {
|
||||
const auto* true_block = TargetBlock(ctx.program, term.true_block);
|
||||
const auto* false_block = TargetBlock(ctx.program, term.false_block);
|
||||
const auto* true_block = TargetBlock(program, term.true_block);
|
||||
const auto* false_block = TargetBlock(program, term.false_block);
|
||||
if (true_block == nullptr || false_block == nullptr || info.condition.IsEmpty()) {
|
||||
EmitReturn(ctx);
|
||||
return;
|
||||
@@ -166,7 +167,7 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
|
||||
|
||||
void EmitDispatcherTarget(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher,
|
||||
const IR::Block* from, uint32_t target) {
|
||||
const auto* block = TargetBlock(ctx.program, target);
|
||||
const auto* block = TargetBlock(ctx.state.program, target);
|
||||
if (block != nullptr) {
|
||||
StoreDispatcherPhiEdge(ctx, dispatcher, from, block);
|
||||
if (ctx.other_half != nullptr) {
|
||||
@@ -239,7 +240,7 @@ void EmitStructuredInstruction(ValueEmitContext& ctx, StructuredFunctionState& s
|
||||
}
|
||||
for (size_t index = 0; index < inst.NumArgs(); index++) {
|
||||
const auto* predecessor = inst.PhiBlock(index);
|
||||
if (predecessor == nullptr || !ctx.labels.contains(predecessor)) {
|
||||
if (predecessor == nullptr || !ctx.state.labels.contains(predecessor)) {
|
||||
ctx.Fail(inst, "has a predecessor outside the structured function");
|
||||
}
|
||||
}
|
||||
@@ -272,10 +273,7 @@ void EmitDispatcherInstruction(ValueEmitContext& ctx, const DispatcherFunctionSt
|
||||
|
||||
template <typename EmitInstruction>
|
||||
void EmitBlock(ValueEmitContext& ctx, const IR::Block* block, EmitInstruction&& emit_instruction) {
|
||||
ctx.current_block = block;
|
||||
if (ctx.other_half != nullptr) {
|
||||
ctx.other_half->current_block = block;
|
||||
}
|
||||
ctx.state.current_block = block;
|
||||
EmitLabel(ctx.state, ctx.Label(block));
|
||||
bool emitted_non_phi = false;
|
||||
for (const auto& inst: *block) {
|
||||
@@ -288,7 +286,6 @@ void EmitBlock(ValueEmitContext& ctx, const IR::Block* block, EmitInstruction&&
|
||||
}
|
||||
for (uint32_t half = 0; half < ctx.state.lane_count; half++) {
|
||||
auto& lane = half == 0 ? ctx : *ctx.other_half;
|
||||
lane.current_block = block;
|
||||
ctx.state.lane_half = half;
|
||||
if (half == 0 || (inst.GetOpcode() != IR::ValueOpcode::Barrier &&
|
||||
inst.GetOpcode() != IR::ValueOpcode::MeshAllocate)) {
|
||||
@@ -315,28 +312,29 @@ void PatchStructuredPhis(ValueEmitContext& ctx, StructuredFunctionState& structu
|
||||
}
|
||||
|
||||
void EmitStructuredFunction(ValueEmitContext& ctx) {
|
||||
const auto& program = ctx.state.program;
|
||||
StructuredFunctionState structured;
|
||||
ctx.state.builder.AddFunction({OpBranch, ctx.Label(ctx.program.blocks.front())});
|
||||
for (size_t index = 0; index < ctx.program.blocks.size(); index++) {
|
||||
const auto* block = ctx.program.blocks[index];
|
||||
ctx.state.builder.AddFunction({OpBranch, ctx.Label(program.blocks.front())});
|
||||
for (size_t index = 0; index < program.blocks.size(); index++) {
|
||||
const auto* block = program.blocks[index];
|
||||
EmitBlock(ctx, block, [&](ValueEmitContext& lane, const IR::Inst& inst) {
|
||||
EmitStructuredInstruction(lane, structured, inst);
|
||||
});
|
||||
structured.block_exit_labels.emplace(block, ctx.state.current_label);
|
||||
EmitStructuredTerminator(ctx, block, ctx.program.block_info[index]);
|
||||
EmitStructuredTerminator(ctx, block, program.block_info[index]);
|
||||
}
|
||||
PatchStructuredPhis(ctx, structured);
|
||||
}
|
||||
|
||||
void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher) {
|
||||
auto& state = ctx.state;
|
||||
const auto* entry = ctx.program.blocks.front();
|
||||
const auto* entry = state.program.blocks.front();
|
||||
state.builder.AddFunction({OpBranch, ctx.Label(entry)});
|
||||
EmitBlock(ctx, entry, [&](ValueEmitContext& lane, const IR::Inst& inst) {
|
||||
EmitDispatcherInstruction(lane, dispatcher, inst);
|
||||
});
|
||||
const auto initial_pc =
|
||||
EmitDispatcherNextPc(ctx, dispatcher, entry, ctx.program.block_info.front());
|
||||
EmitDispatcherNextPc(ctx, dispatcher, entry, state.program.block_info.front());
|
||||
const auto initial_parent = state.current_label;
|
||||
state.builder.AddFunction({OpBranch, dispatcher.header_label});
|
||||
|
||||
@@ -357,21 +355,21 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState
|
||||
state.builder.AddFunction(
|
||||
{OpSelectionMerge, dispatcher.after_switch_label, SelectionControlNone});
|
||||
std::vector<uint32_t> words {OpSwitch, pc, dispatcher.after_switch_label};
|
||||
for (size_t index = 1; index < ctx.program.blocks.size(); index++) {
|
||||
words.push_back(ctx.program.block_info[index].id);
|
||||
words.push_back(ctx.Label(ctx.program.blocks[index]));
|
||||
for (size_t index = 1; index < state.program.blocks.size(); index++) {
|
||||
words.push_back(state.program.block_info[index].id);
|
||||
words.push_back(ctx.Label(state.program.blocks[index]));
|
||||
}
|
||||
state.builder.AddFunction(words);
|
||||
std::vector<uint32_t> next_pc_words {OpPhi, TypeU32(state), next_pc,
|
||||
ConstantU32(state, UINT32_MAX), dispatcher.select_label};
|
||||
|
||||
for (size_t index = 1; index < ctx.program.blocks.size(); index++) {
|
||||
EmitBlock(ctx, ctx.program.blocks[index],
|
||||
for (size_t index = 1; index < state.program.blocks.size(); index++) {
|
||||
EmitBlock(ctx, state.program.blocks[index],
|
||||
[&](ValueEmitContext& lane, const IR::Inst& inst) {
|
||||
EmitDispatcherInstruction(lane, dispatcher, inst);
|
||||
});
|
||||
const auto selected = EmitDispatcherNextPc(ctx, dispatcher, ctx.program.blocks[index],
|
||||
ctx.program.block_info[index]);
|
||||
const auto selected = EmitDispatcherNextPc(ctx, dispatcher, state.program.blocks[index],
|
||||
state.program.block_info[index]);
|
||||
next_pc_words.push_back(selected);
|
||||
next_pc_words.push_back(state.current_label);
|
||||
state.builder.AddFunction({OpBranch, dispatcher.after_switch_label});
|
||||
@@ -423,8 +421,8 @@ uint32_t ValueEmitContext::Def(IR::Value value) {
|
||||
if (inst == nullptr) {
|
||||
Fail("direct SPIR-V emitter received a non-value argument");
|
||||
}
|
||||
if (dispatcher_spills != nullptr && current_block != nullptr &&
|
||||
inst->Parent() != current_block) {
|
||||
if (dispatcher_spills != nullptr && state.current_block != nullptr &&
|
||||
inst->Parent() != state.current_block) {
|
||||
if (const auto found = dispatcher_spills->find(inst); found != dispatcher_spills->end()) {
|
||||
if (const auto loaded = dispatcher_block_loads.find(inst);
|
||||
loaded != dispatcher_block_loads.end() &&
|
||||
@@ -437,12 +435,7 @@ uint32_t ValueEmitContext::Def(IR::Value value) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
if (const auto found = definitions.find(inst); found != definitions.end()) {
|
||||
return found->second;
|
||||
}
|
||||
const auto id = state.builder.AllocateId();
|
||||
definitions.emplace(inst, id);
|
||||
return id;
|
||||
return Result(*inst);
|
||||
}
|
||||
|
||||
uint32_t ValueEmitContext::Arg(const IR::Inst& inst, size_t index) {
|
||||
@@ -537,10 +530,12 @@ uint32_t ValueEmitContext::Result(const IR::Inst& inst) {
|
||||
|
||||
uint32_t ValueEmitContext::Emit(const IR::Inst& inst, uint32_t opcode, IR::Type type,
|
||||
std::initializer_list<uint32_t> args) {
|
||||
std::vector<uint32_t> words {opcode, TypeId(type), Result(inst)};
|
||||
const auto type_id = TypeId(type);
|
||||
const auto result = Result(inst);
|
||||
std::vector<uint32_t> words {opcode, type_id, result};
|
||||
words.insert(words.end(), args.begin(), args.end());
|
||||
state.builder.AddFunction(words);
|
||||
return Result(inst);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t ValueEmitContext::Define(const IR::Inst& inst, uint32_t value) {
|
||||
@@ -571,33 +566,34 @@ const IR::Inst* ValueEmitContext::ImageAddress(IR::Value value) {
|
||||
}
|
||||
|
||||
const IR::MemoryInfo& ValueEmitContext::Memory(const IR::Inst& inst) const {
|
||||
return program.memory_info.at(inst.Flags<IR::MemoryFlags>().index);
|
||||
return state.program.memory_info.at(inst.Flags<IR::MemoryFlags>().index);
|
||||
}
|
||||
|
||||
const IR::ExportInfo& ValueEmitContext::Export(const IR::Inst& inst) const {
|
||||
return program.export_info.at(inst.Flags<IR::ExportFlags>().index);
|
||||
return state.program.export_info.at(inst.Flags<IR::ExportFlags>().index);
|
||||
}
|
||||
|
||||
uint32_t ValueEmitContext::Label(const IR::Block* block) const {
|
||||
return labels.at(block);
|
||||
return state.labels.at(block);
|
||||
}
|
||||
|
||||
[[noreturn]] void ValueEmitContext::Fail(const char* reason) const {
|
||||
EXIT("SPIR-V emission failed: hash=0x%016" PRIx64 " stage=%u reason=%s\n",
|
||||
program.shader_hash, static_cast<unsigned>(program.stage), reason);
|
||||
state.program.shader_hash, static_cast<unsigned>(state.program.stage), reason);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
[[noreturn]] void ValueEmitContext::Fail(const IR::Inst& inst, const char* reason) const {
|
||||
EXIT("SPIR-V emission failed: hash=0x%016" PRIx64 " stage=%u opcode=%s reason=%s\n",
|
||||
program.shader_hash, static_cast<unsigned>(program.stage),
|
||||
state.program.shader_hash, static_cast<unsigned>(state.program.stage),
|
||||
IR::ValueOpcodeName(inst.GetOpcode()), reason);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void EmitProgram(EmitterState& state, const IR::Program& program) {
|
||||
ValueEmitContext ctx(state, program);
|
||||
ValueEmitContext high(state, program);
|
||||
void EmitProgram(EmitterState& state) {
|
||||
const auto& program = state.program;
|
||||
ValueEmitContext ctx(state);
|
||||
ValueEmitContext high(state);
|
||||
if (state.lane_count == 2) {
|
||||
ctx.other_half = &high;
|
||||
high.other_half = &ctx;
|
||||
@@ -610,8 +606,7 @@ void EmitProgram(EmitterState& state, const IR::Program& program) {
|
||||
}
|
||||
for (const auto* block: program.blocks) {
|
||||
const auto label = state.builder.AllocateId();
|
||||
ctx.labels.emplace(block, label);
|
||||
high.labels.emplace(block, label);
|
||||
state.labels.emplace(block, label);
|
||||
}
|
||||
if (state.program.dispatcher_fallback) {
|
||||
auto& dispatch = dispatcher.emplace();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <span>
|
||||
#include <stack>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::CFG {
|
||||
@@ -323,11 +324,10 @@ bool ResolveJumpTableEntryCount(const Decoder::Program& program, uint32_t before
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AddUniqueTargetPc(std::vector<uint32_t>& targets, uint32_t target) {
|
||||
if (std::find(targets.begin(), targets.end(), target) == targets.end()) {
|
||||
targets.push_back(target);
|
||||
void AddUnique(std::vector<uint32_t>& values, uint32_t value) {
|
||||
if (std::find(values.begin(), values.end(), value) == values.end()) {
|
||||
values.push_back(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScalarCodeWrittenInRange(const Decoder::Program& program, uint32_t begin_index,
|
||||
@@ -421,7 +421,7 @@ bool ResolveSetpcJumpTable(const Decoder::Program& program, uint32_t setpc_index
|
||||
return false;
|
||||
}
|
||||
const auto target_pc = (target_base + low) & ~3u;
|
||||
AddUniqueTargetPc(targets, target_pc);
|
||||
AddUnique(targets, target_pc);
|
||||
selector_values.push_back(i * 8u);
|
||||
selector_target_pcs.push_back(target_pc);
|
||||
}
|
||||
@@ -507,7 +507,7 @@ bool ResolveSetpcDwordJumpTable(const Decoder::Program& program, uint32_t setpc_
|
||||
return false;
|
||||
}
|
||||
const auto target_pc = (target_base - offset) & ~3u;
|
||||
AddUniqueTargetPc(targets, target_pc);
|
||||
AddUnique(targets, target_pc);
|
||||
selector_values.push_back(i * 4u);
|
||||
selector_target_pcs.push_back(target_pc);
|
||||
}
|
||||
@@ -590,12 +590,6 @@ bool IsValidTarget(uint32_t target, const std::set<uint32_t>& instruction_pcs, u
|
||||
return target == end_pc || (target >= first_pc && instruction_pcs.contains(target));
|
||||
}
|
||||
|
||||
void AddUnique(std::vector<uint32_t>& values, uint32_t value) {
|
||||
if (std::find(values.begin(), values.end(), value) == values.end()) {
|
||||
values.push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> AllBlockIds(uint32_t count) {
|
||||
std::vector<uint32_t> ids;
|
||||
ids.reserve(count);
|
||||
@@ -868,15 +862,15 @@ void ComputeNaturalLoops(Graph& graph) {
|
||||
loop.header = edge.to;
|
||||
loop.latch = edge.from;
|
||||
loop.continue_block = edge.from;
|
||||
loop.body_blocks = body;
|
||||
loop.body_blocks = std::move(body);
|
||||
|
||||
for (auto block_id: body) {
|
||||
for (auto block_id: loop.body_blocks) {
|
||||
const auto* block = graph.FindBlock(block_id);
|
||||
if (block == nullptr) {
|
||||
continue;
|
||||
}
|
||||
for (auto succ: block->successors) {
|
||||
if (!Contains(body, succ)) {
|
||||
if (!Contains(loop.body_blocks, succ)) {
|
||||
AddUnique(loop.exit_blocks, succ);
|
||||
}
|
||||
}
|
||||
@@ -995,6 +989,29 @@ void RecomputeAnalyses(Graph& graph) {
|
||||
ComputeComponents(graph);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> ApplyBlockOrder(Graph& graph, std::vector<BasicBlock> blocks) {
|
||||
std::vector<uint32_t> id_map(blocks.size(), UINT32_MAX);
|
||||
for (uint32_t i = 0; i < blocks.size(); i++) {
|
||||
id_map[blocks[i].id] = i;
|
||||
}
|
||||
|
||||
graph.blocks = std::move(blocks);
|
||||
graph.entry_block = RemapId(graph.entry_block, id_map);
|
||||
for (auto& block: graph.blocks) {
|
||||
block.id = RemapId(block.id, id_map);
|
||||
RemapIds(block.predecessors, id_map);
|
||||
RemapIds(block.successors, id_map);
|
||||
RemapIds(block.dominators, id_map);
|
||||
RemapIds(block.post_dominators, id_map);
|
||||
block.terminator.true_block = RemapId(block.terminator.true_block, id_map);
|
||||
block.terminator.false_block = RemapId(block.terminator.false_block, id_map);
|
||||
block.terminator.merge_block = RemapId(block.terminator.merge_block, id_map);
|
||||
block.terminator.continue_block = RemapId(block.terminator.continue_block, id_map);
|
||||
}
|
||||
|
||||
return id_map;
|
||||
}
|
||||
|
||||
uint32_t MoveBlockBefore(Graph& graph, uint32_t block_id, uint32_t before_id) {
|
||||
if (block_id == before_id || block_id >= graph.blocks.size() ||
|
||||
before_id >= graph.blocks.size()) {
|
||||
@@ -1016,24 +1033,7 @@ uint32_t MoveBlockBefore(Graph& graph, uint32_t block_id, uint32_t before_id) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> id_map(new_blocks.size(), UINT32_MAX);
|
||||
for (uint32_t i = 0; i < new_blocks.size(); i++) {
|
||||
id_map[new_blocks[i].id] = i;
|
||||
}
|
||||
|
||||
graph.blocks = std::move(new_blocks);
|
||||
graph.entry_block = RemapId(graph.entry_block, id_map);
|
||||
for (auto& block: graph.blocks) {
|
||||
block.id = RemapId(block.id, id_map);
|
||||
RemapIds(block.predecessors, id_map);
|
||||
RemapIds(block.successors, id_map);
|
||||
RemapIds(block.dominators, id_map);
|
||||
RemapIds(block.post_dominators, id_map);
|
||||
block.terminator.true_block = RemapId(block.terminator.true_block, id_map);
|
||||
block.terminator.false_block = RemapId(block.terminator.false_block, id_map);
|
||||
block.terminator.merge_block = RemapId(block.terminator.merge_block, id_map);
|
||||
block.terminator.continue_block = RemapId(block.terminator.continue_block, id_map);
|
||||
}
|
||||
const auto id_map = ApplyBlockOrder(graph, std::move(new_blocks));
|
||||
|
||||
return RemapId(block_id, id_map);
|
||||
}
|
||||
@@ -1396,16 +1396,16 @@ bool SplitSharedMergeBlock(Graph& graph, uint32_t merge,
|
||||
}
|
||||
|
||||
std::vector<uint32_t> construct_predecessors;
|
||||
std::vector<uint32_t> external_predecessors;
|
||||
bool has_external_predecessor = false;
|
||||
for (auto pred: merge_block->predecessors) {
|
||||
if (Contains(construct_blocks, pred)) {
|
||||
AddUnique(construct_predecessors, pred);
|
||||
} else {
|
||||
AddUnique(external_predecessors, pred);
|
||||
has_external_predecessor = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (construct_predecessors.empty() || (!force_split && external_predecessors.empty())) {
|
||||
if (construct_predecessors.empty() || (!force_split && !has_external_predecessor)) {
|
||||
return false;
|
||||
}
|
||||
const auto synthetic_merge = AppendSyntheticBranchBlock(graph, merge);
|
||||
@@ -1656,24 +1656,7 @@ void PlaceGotoRouteBlocks(Graph& graph, uint32_t original_block_count,
|
||||
new_blocks.push_back(std::move(old_blocks[block_id]));
|
||||
}
|
||||
|
||||
std::vector<uint32_t> id_map(new_blocks.size(), UINT32_MAX);
|
||||
for (uint32_t block_id = 0; block_id < new_blocks.size(); block_id++) {
|
||||
id_map[new_blocks[block_id].id] = block_id;
|
||||
}
|
||||
|
||||
graph.blocks = std::move(new_blocks);
|
||||
graph.entry_block = RemapId(graph.entry_block, id_map);
|
||||
for (auto& block: graph.blocks) {
|
||||
block.id = RemapId(block.id, id_map);
|
||||
RemapIds(block.predecessors, id_map);
|
||||
RemapIds(block.successors, id_map);
|
||||
RemapIds(block.dominators, id_map);
|
||||
RemapIds(block.post_dominators, id_map);
|
||||
block.terminator.true_block = RemapId(block.terminator.true_block, id_map);
|
||||
block.terminator.false_block = RemapId(block.terminator.false_block, id_map);
|
||||
block.terminator.merge_block = RemapId(block.terminator.merge_block, id_map);
|
||||
block.terminator.continue_block = RemapId(block.terminator.continue_block, id_map);
|
||||
}
|
||||
ApplyBlockOrder(graph, std::move(new_blocks));
|
||||
}
|
||||
|
||||
uint32_t AppendGotoSelectBlock(Graph& graph, uint32_t route_variable, uint32_t true_target,
|
||||
@@ -1767,7 +1750,7 @@ bool RouteOneSharedArm(Graph& graph, uint32_t original_block_count, uint32_t out
|
||||
inner_is_true ? outer->terminator.false_block : outer->terminator.true_block;
|
||||
const auto* inner = graph.FindBlock(inner_id);
|
||||
if (inner == nullptr || inner->inst_begin == inner->inst_end ||
|
||||
inner->predecessors != std::vector<uint32_t> {outer_id} ||
|
||||
inner->predecessors.size() != 1u || inner->predecessors.front() != outer_id ||
|
||||
inner->terminator.kind != TerminatorKind::ConditionalBranch ||
|
||||
IsInnermostLoopControlConditional(graph, *inner)) {
|
||||
continue;
|
||||
@@ -1959,9 +1942,9 @@ Graph BuildGraph(const Decoder::Program& program) {
|
||||
graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
|
||||
fmt::format("unsupported dynamic S_SETPC_B64 at pc 0x{:08x}", inst.pc));
|
||||
}
|
||||
const auto& target_pcs = target_info.indirect
|
||||
? target_info.target_pcs
|
||||
: std::vector<uint32_t> {target_info.target};
|
||||
const auto target_pcs = target_info.indirect
|
||||
? std::span<const uint32_t>(target_info.target_pcs)
|
||||
: std::span<const uint32_t>(&target_info.target, 1);
|
||||
for (const auto target: target_pcs) {
|
||||
if (!IsValidTarget(target, instruction_pcs, first_pc, end_pc)) {
|
||||
ExitBuildFailure(
|
||||
@@ -1981,7 +1964,6 @@ Graph BuildGraph(const Decoder::Program& program) {
|
||||
}
|
||||
|
||||
std::vector<uint32_t> sorted_labels(labels.begin(), labels.end());
|
||||
std::sort(sorted_labels.begin(), sorted_labels.end());
|
||||
for (uint32_t i = 0; i < sorted_labels.size(); i++) {
|
||||
const auto start = sorted_labels[i];
|
||||
if (start > end_pc) {
|
||||
@@ -2122,11 +2104,7 @@ Graph BuildGraph(const Decoder::Program& program) {
|
||||
}
|
||||
SortUnique(graph.code_table_load_pcs);
|
||||
|
||||
ComputeDominators(graph);
|
||||
ComputePostDominators(graph);
|
||||
ComputeBackEdges(graph);
|
||||
ComputeNaturalLoops(graph);
|
||||
ComputeComponents(graph);
|
||||
RecomputeAnalyses(graph);
|
||||
|
||||
if (indirect_setpc) {
|
||||
graph.irreducible = true;
|
||||
|
||||
@@ -10,7 +10,6 @@ void DecodeExp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
const uint32_t en = word0 & 0xfu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = 2;
|
||||
inst.family = Family::EXP;
|
||||
inst.opcode_id = target;
|
||||
|
||||
@@ -8,24 +8,15 @@ namespace Libs::Graphics::ShaderRecompiler::Decoder {
|
||||
namespace {
|
||||
|
||||
struct MimgSampleInfo {
|
||||
uint32_t encoding = 0;
|
||||
const char* name = nullptr;
|
||||
uint32_t flags = 0;
|
||||
uint32_t address_components = 3;
|
||||
uint32_t encoding = 0;
|
||||
const char* name = nullptr;
|
||||
uint32_t flags = 0;
|
||||
};
|
||||
|
||||
struct MimgGatherInfo {
|
||||
uint32_t encoding = 0;
|
||||
const char* name = nullptr;
|
||||
Opcode decoded = Opcode::UNSUPPORTED;
|
||||
uint32_t flags = 0;
|
||||
uint32_t address_components = 3;
|
||||
};
|
||||
|
||||
struct MimgAtomicInfo {
|
||||
uint32_t encoding = 0;
|
||||
const char* name = nullptr;
|
||||
Opcode decoded = Opcode::UNSUPPORTED;
|
||||
uint32_t encoding = 0;
|
||||
Opcode decoded = Opcode::UNSUPPORTED;
|
||||
uint32_t flags = 0;
|
||||
};
|
||||
|
||||
constexpr ImageDimension DecodeImageDimension(uint32_t dim) {
|
||||
@@ -84,131 +75,127 @@ constexpr uint32_t ImageSampleAddressComponents(uint32_t flags, ImageDimension d
|
||||
return components;
|
||||
}
|
||||
|
||||
constexpr MimgSampleInfo SampleInfo(uint32_t opcode, const char* name, uint32_t flags) {
|
||||
return {opcode, name, flags, ImageSampleAddressComponents(flags, ImageDimension::Dim2D)};
|
||||
}
|
||||
|
||||
constexpr MimgSampleInfo MIMG_SAMPLE_OPCODE_LIST[] = {
|
||||
SampleInfo(0x20u, "image_sample", 0),
|
||||
SampleInfo(0x21u, "image_sample_cl", ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x22u, "image_sample_d", ImageSampleFlagDerivative),
|
||||
SampleInfo(0x23u, "image_sample_d_cl", ImageSampleFlagDerivative | ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x24u, "image_sample_l", ImageSampleFlagLod),
|
||||
SampleInfo(0x25u, "image_sample_b", ImageSampleFlagBias),
|
||||
SampleInfo(0x26u, "image_sample_b_cl", ImageSampleFlagBias | ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x27u, "image_sample_lz", ImageSampleFlagLevelZero),
|
||||
SampleInfo(0x28u, "image_sample_c", ImageSampleFlagCompare),
|
||||
SampleInfo(0x29u, "image_sample_c_cl", ImageSampleFlagCompare | ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x2au, "image_sample_c_d", ImageSampleFlagCompare | ImageSampleFlagDerivative),
|
||||
SampleInfo(0x2bu, "image_sample_c_d_cl",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x2cu, "image_sample_c_l", ImageSampleFlagCompare | ImageSampleFlagLod),
|
||||
SampleInfo(0x2du, "image_sample_c_b", ImageSampleFlagCompare | ImageSampleFlagBias),
|
||||
SampleInfo(0x2eu, "image_sample_c_b_cl",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x2fu, "image_sample_c_lz", ImageSampleFlagCompare | ImageSampleFlagLevelZero),
|
||||
SampleInfo(0x30u, "image_sample_o", ImageSampleFlagOffset),
|
||||
SampleInfo(0x31u, "image_sample_cl_o", ImageSampleFlagLodClamp | ImageSampleFlagOffset),
|
||||
SampleInfo(0x32u, "image_sample_d_o", ImageSampleFlagDerivative | ImageSampleFlagOffset),
|
||||
SampleInfo(0x33u, "image_sample_d_cl_o",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagLodClamp | ImageSampleFlagOffset),
|
||||
SampleInfo(0x34u, "image_sample_l_o", ImageSampleFlagLod | ImageSampleFlagOffset),
|
||||
SampleInfo(0x35u, "image_sample_b_o", ImageSampleFlagBias | ImageSampleFlagOffset),
|
||||
SampleInfo(0x36u, "image_sample_b_cl_o",
|
||||
ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagOffset),
|
||||
SampleInfo(0x37u, "image_sample_lz_o", ImageSampleFlagLevelZero | ImageSampleFlagOffset),
|
||||
SampleInfo(0x38u, "image_sample_c_o", ImageSampleFlagCompare | ImageSampleFlagOffset),
|
||||
SampleInfo(0x39u, "image_sample_c_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagOffset),
|
||||
SampleInfo(0x3au, "image_sample_c_d_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagOffset),
|
||||
SampleInfo(0x3bu, "image_sample_c_d_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset),
|
||||
SampleInfo(0x3cu, "image_sample_c_l_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLod | ImageSampleFlagOffset),
|
||||
SampleInfo(0x3du, "image_sample_c_b_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagOffset),
|
||||
SampleInfo(0x3eu, "image_sample_c_b_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset),
|
||||
SampleInfo(0x3fu, "image_sample_c_lz_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLevelZero | ImageSampleFlagOffset),
|
||||
SampleInfo(0x68u, "image_sample_cd", ImageSampleFlagDerivative | ImageSampleFlagCd),
|
||||
SampleInfo(0x69u, "image_sample_cd_cl",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x6au, "image_sample_c_cd",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd),
|
||||
SampleInfo(0x6bu, "image_sample_c_cd_cl",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd |
|
||||
ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x6cu, "image_sample_cd_o",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagOffset),
|
||||
SampleInfo(0x6du, "image_sample_cd_cl_o",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset),
|
||||
SampleInfo(0x6eu, "image_sample_c_cd_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd |
|
||||
ImageSampleFlagOffset),
|
||||
SampleInfo(0x6fu, "image_sample_c_cd_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd |
|
||||
ImageSampleFlagLodClamp | ImageSampleFlagOffset),
|
||||
SampleInfo(0xa0u, "image_sample_a", ImageSampleFlagAdjust),
|
||||
SampleInfo(0xa1u, "image_sample_cl_a", ImageSampleFlagLodClamp | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xa5u, "image_sample_b_a", ImageSampleFlagBias | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xa6u, "image_sample_b_cl_a",
|
||||
ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xa8u, "image_sample_c_a", ImageSampleFlagCompare | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xa9u, "image_sample_c_cl_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xadu, "image_sample_c_b_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xaeu, "image_sample_c_b_cl_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagAdjust),
|
||||
SampleInfo(0xb0u, "image_sample_o_a", ImageSampleFlagOffset | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xb1u, "image_sample_cl_o_a",
|
||||
ImageSampleFlagLodClamp | ImageSampleFlagOffset | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xb5u, "image_sample_b_o_a",
|
||||
ImageSampleFlagBias | ImageSampleFlagOffset | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xb6u, "image_sample_b_cl_o_a",
|
||||
ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagOffset |
|
||||
ImageSampleFlagAdjust),
|
||||
SampleInfo(0xb8u, "image_sample_c_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagOffset | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xb9u, "image_sample_c_cl_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagOffset |
|
||||
ImageSampleFlagAdjust),
|
||||
SampleInfo(0xbdu, "image_sample_c_b_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagOffset |
|
||||
ImageSampleFlagAdjust),
|
||||
SampleInfo(0xbeu, "image_sample_c_b_cl_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset | ImageSampleFlagAdjust),
|
||||
{0x20u, "image_sample", 0},
|
||||
{0x21u, "image_sample_cl", ImageSampleFlagLodClamp},
|
||||
{0x22u, "image_sample_d", ImageSampleFlagDerivative},
|
||||
{0x23u, "image_sample_d_cl", ImageSampleFlagDerivative | ImageSampleFlagLodClamp},
|
||||
{0x24u, "image_sample_l", ImageSampleFlagLod},
|
||||
{0x25u, "image_sample_b", ImageSampleFlagBias},
|
||||
{0x26u, "image_sample_b_cl", ImageSampleFlagBias | ImageSampleFlagLodClamp},
|
||||
{0x27u, "image_sample_lz", ImageSampleFlagLevelZero},
|
||||
{0x28u, "image_sample_c", ImageSampleFlagCompare},
|
||||
{0x29u, "image_sample_c_cl", ImageSampleFlagCompare | ImageSampleFlagLodClamp},
|
||||
{0x2au, "image_sample_c_d", ImageSampleFlagCompare | ImageSampleFlagDerivative},
|
||||
{0x2bu, "image_sample_c_d_cl",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagLodClamp},
|
||||
{0x2cu, "image_sample_c_l", ImageSampleFlagCompare | ImageSampleFlagLod},
|
||||
{0x2du, "image_sample_c_b", ImageSampleFlagCompare | ImageSampleFlagBias},
|
||||
{0x2eu, "image_sample_c_b_cl",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp},
|
||||
{0x2fu, "image_sample_c_lz", ImageSampleFlagCompare | ImageSampleFlagLevelZero},
|
||||
{0x30u, "image_sample_o", ImageSampleFlagOffset},
|
||||
{0x31u, "image_sample_cl_o", ImageSampleFlagLodClamp | ImageSampleFlagOffset},
|
||||
{0x32u, "image_sample_d_o", ImageSampleFlagDerivative | ImageSampleFlagOffset},
|
||||
{0x33u, "image_sample_d_cl_o",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagLodClamp | ImageSampleFlagOffset},
|
||||
{0x34u, "image_sample_l_o", ImageSampleFlagLod | ImageSampleFlagOffset},
|
||||
{0x35u, "image_sample_b_o", ImageSampleFlagBias | ImageSampleFlagOffset},
|
||||
{0x36u, "image_sample_b_cl_o",
|
||||
ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagOffset},
|
||||
{0x37u, "image_sample_lz_o", ImageSampleFlagLevelZero | ImageSampleFlagOffset},
|
||||
{0x38u, "image_sample_c_o", ImageSampleFlagCompare | ImageSampleFlagOffset},
|
||||
{0x39u, "image_sample_c_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagOffset},
|
||||
{0x3au, "image_sample_c_d_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagOffset},
|
||||
{0x3bu, "image_sample_c_d_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset},
|
||||
{0x3cu, "image_sample_c_l_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLod | ImageSampleFlagOffset},
|
||||
{0x3du, "image_sample_c_b_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagOffset},
|
||||
{0x3eu, "image_sample_c_b_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset},
|
||||
{0x3fu, "image_sample_c_lz_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLevelZero | ImageSampleFlagOffset},
|
||||
{0x68u, "image_sample_cd", ImageSampleFlagDerivative | ImageSampleFlagCd},
|
||||
{0x69u, "image_sample_cd_cl",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagLodClamp},
|
||||
{0x6au, "image_sample_c_cd",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd},
|
||||
{0x6bu, "image_sample_c_cd_cl",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd |
|
||||
ImageSampleFlagLodClamp},
|
||||
{0x6cu, "image_sample_cd_o",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagOffset},
|
||||
{0x6du, "image_sample_cd_cl_o",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset},
|
||||
{0x6eu, "image_sample_c_cd_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd |
|
||||
ImageSampleFlagOffset},
|
||||
{0x6fu, "image_sample_c_cd_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd |
|
||||
ImageSampleFlagLodClamp | ImageSampleFlagOffset},
|
||||
{0xa0u, "image_sample_a", ImageSampleFlagAdjust},
|
||||
{0xa1u, "image_sample_cl_a", ImageSampleFlagLodClamp | ImageSampleFlagAdjust},
|
||||
{0xa5u, "image_sample_b_a", ImageSampleFlagBias | ImageSampleFlagAdjust},
|
||||
{0xa6u, "image_sample_b_cl_a",
|
||||
ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagAdjust},
|
||||
{0xa8u, "image_sample_c_a", ImageSampleFlagCompare | ImageSampleFlagAdjust},
|
||||
{0xa9u, "image_sample_c_cl_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagAdjust},
|
||||
{0xadu, "image_sample_c_b_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagAdjust},
|
||||
{0xaeu, "image_sample_c_b_cl_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagAdjust},
|
||||
{0xb0u, "image_sample_o_a", ImageSampleFlagOffset | ImageSampleFlagAdjust},
|
||||
{0xb1u, "image_sample_cl_o_a",
|
||||
ImageSampleFlagLodClamp | ImageSampleFlagOffset | ImageSampleFlagAdjust},
|
||||
{0xb5u, "image_sample_b_o_a",
|
||||
ImageSampleFlagBias | ImageSampleFlagOffset | ImageSampleFlagAdjust},
|
||||
{0xb6u, "image_sample_b_cl_o_a",
|
||||
ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagOffset |
|
||||
ImageSampleFlagAdjust},
|
||||
{0xb8u, "image_sample_c_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagOffset | ImageSampleFlagAdjust},
|
||||
{0xb9u, "image_sample_c_cl_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagOffset |
|
||||
ImageSampleFlagAdjust},
|
||||
{0xbdu, "image_sample_c_b_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagOffset |
|
||||
ImageSampleFlagAdjust},
|
||||
{0xbeu, "image_sample_c_b_cl_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset | ImageSampleFlagAdjust},
|
||||
};
|
||||
|
||||
constexpr MimgGatherInfo MIMG_GATHER_OPCODE_LIST[] = {
|
||||
{0x47u, "image_gather4_lz", Opcode::IMAGE_GATHER4_LZ, ImageSampleFlagLevelZero, 2u},
|
||||
{0x48u, "image_gather4_c", Opcode::IMAGE_GATHER4_C, ImageSampleFlagCompare, 3u},
|
||||
{0x4fu, "image_gather4_c_lz", Opcode::IMAGE_GATHER4_C_LZ,
|
||||
ImageSampleFlagCompare | ImageSampleFlagLevelZero, 3u},
|
||||
{0x57u, "image_gather4_lz_o", Opcode::IMAGE_GATHER4_LZ_O,
|
||||
ImageSampleFlagLevelZero | ImageSampleFlagOffset, 3u},
|
||||
{0x58u, "image_gather4_c_o", Opcode::IMAGE_GATHER4_C_O,
|
||||
ImageSampleFlagCompare | ImageSampleFlagOffset, 4u},
|
||||
{0x5fu, "image_gather4_c_lz_o", Opcode::IMAGE_GATHER4_C_LZ_O,
|
||||
ImageSampleFlagCompare | ImageSampleFlagLevelZero | ImageSampleFlagOffset, 4u},
|
||||
{0x61u, "image_gather4h", Opcode::IMAGE_GATHER4H, ImageSampleFlagGatherHorizontal, 2u},
|
||||
{0x47u, Opcode::IMAGE_GATHER4_LZ, ImageSampleFlagLevelZero},
|
||||
{0x48u, Opcode::IMAGE_GATHER4_C, ImageSampleFlagCompare},
|
||||
{0x4fu, Opcode::IMAGE_GATHER4_C_LZ,
|
||||
ImageSampleFlagCompare | ImageSampleFlagLevelZero},
|
||||
{0x57u, Opcode::IMAGE_GATHER4_LZ_O,
|
||||
ImageSampleFlagLevelZero | ImageSampleFlagOffset},
|
||||
{0x58u, Opcode::IMAGE_GATHER4_C_O,
|
||||
ImageSampleFlagCompare | ImageSampleFlagOffset},
|
||||
{0x5fu, Opcode::IMAGE_GATHER4_C_LZ_O,
|
||||
ImageSampleFlagCompare | ImageSampleFlagLevelZero | ImageSampleFlagOffset},
|
||||
{0x61u, Opcode::IMAGE_GATHER4H, ImageSampleFlagGatherHorizontal},
|
||||
};
|
||||
|
||||
constexpr MimgAtomicInfo MIMG_ATOMIC_OPCODE_LIST[] = {
|
||||
{0x0fu, "image_atomic_swap", Opcode::IMAGE_ATOMIC_SWAP},
|
||||
{0x11u, "image_atomic_add", Opcode::IMAGE_ATOMIC_ADD},
|
||||
{0x15u, "image_atomic_umin", Opcode::IMAGE_ATOMIC_UMIN},
|
||||
{0x17u, "image_atomic_umax", Opcode::IMAGE_ATOMIC_UMAX},
|
||||
{0x18u, "image_atomic_and", Opcode::IMAGE_ATOMIC_AND},
|
||||
{0x19u, "image_atomic_or", Opcode::IMAGE_ATOMIC_OR},
|
||||
{0x1au, "image_atomic_xor", Opcode::IMAGE_ATOMIC_XOR},
|
||||
constexpr Detail::OpcodeMap MIMG_ATOMIC_OPCODE_LIST[] = {
|
||||
{0x0fu, Opcode::IMAGE_ATOMIC_SWAP},
|
||||
{0x11u, Opcode::IMAGE_ATOMIC_ADD},
|
||||
{0x15u, Opcode::IMAGE_ATOMIC_UMIN},
|
||||
{0x17u, Opcode::IMAGE_ATOMIC_UMAX},
|
||||
{0x18u, Opcode::IMAGE_ATOMIC_AND},
|
||||
{0x19u, Opcode::IMAGE_ATOMIC_OR},
|
||||
{0x1au, Opcode::IMAGE_ATOMIC_XOR},
|
||||
};
|
||||
|
||||
constexpr auto MIMG_SAMPLE_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_SAMPLE_OPCODE_LIST);
|
||||
@@ -223,12 +210,12 @@ const MimgGatherInfo* LookupGather(uint32_t opcode) {
|
||||
return Detail::FindOpcode(MIMG_GATHER_OPS, opcode);
|
||||
}
|
||||
|
||||
const MimgAtomicInfo* LookupAtomic(uint32_t opcode) {
|
||||
const Detail::OpcodeMap* LookupAtomic(uint32_t opcode) {
|
||||
return Detail::FindOpcode(MIMG_ATOMIC_OPS, opcode);
|
||||
}
|
||||
|
||||
Opcode DecodeMimgOpcode(uint32_t opcode, const MimgSampleInfo* sample, const MimgGatherInfo* gather,
|
||||
const MimgAtomicInfo* atomic) {
|
||||
const Detail::OpcodeMap* atomic) {
|
||||
if (sample != nullptr) {
|
||||
return Opcode::IMAGE_SAMPLE;
|
||||
}
|
||||
@@ -262,7 +249,7 @@ uint32_t DecodeMimgSampleFlags(const MimgSampleInfo* sample, const MimgGatherInf
|
||||
|
||||
uint32_t DecodeMimgAddressComponents(uint32_t opcode, ImageDimension dimension,
|
||||
const MimgSampleInfo* sample, const MimgGatherInfo* gather,
|
||||
const MimgAtomicInfo* atomic) {
|
||||
const Detail::OpcodeMap* atomic) {
|
||||
if (sample != nullptr) {
|
||||
return ImageSampleAddressComponents(sample->flags, dimension);
|
||||
}
|
||||
@@ -349,7 +336,6 @@ void DecodeMimg(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const auto* atomic = LookupAtomic(opcode);
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = word_count;
|
||||
inst.family = Family::MIMG;
|
||||
inst.opcode_id = opcode;
|
||||
|
||||
@@ -129,13 +129,6 @@ uint32_t SignExtendU32(uint32_t value, uint32_t bits) {
|
||||
return (value ^ sign) - sign;
|
||||
}
|
||||
|
||||
void MarkMemoryUnsupported(Instruction& inst, Family family, uint32_t opcode, const char* reason) {
|
||||
inst.family = family;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Opcode::UNSUPPORTED;
|
||||
SetUnsupported(inst, family, opcode, reason);
|
||||
}
|
||||
|
||||
void ApplyMemoryInfo(Instruction& inst, const MemoryOpcodeInfo* info) {
|
||||
if (info == nullptr) {
|
||||
inst.opcode = Opcode::UNSUPPORTED;
|
||||
@@ -231,7 +224,6 @@ void DecodeSmem(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t soffset = (word1 >> 25u) & 0x7fu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = 2;
|
||||
inst.offset = SignExtendU32(word1 & 0x1fffffu, 21u);
|
||||
inst.glc = ((word0 >> 16u) & 1u) != 0;
|
||||
@@ -241,7 +233,7 @@ void DecodeSmem(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
ApplyMemoryInfo(inst, info);
|
||||
SetRawWords(inst, code, word_index, 2);
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
MarkMemoryUnsupported(inst, Family::SMEM, opcode, "SMEM opcode is not implemented");
|
||||
SetUnsupported(inst, Family::SMEM, opcode, "SMEM opcode is not implemented");
|
||||
}
|
||||
|
||||
DecodeScalarDestination(sdst, pc, inst.dst);
|
||||
@@ -263,7 +255,6 @@ void DecodeMubuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
const uint32_t soffset = (word1 >> 24u) & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = 2;
|
||||
inst.offset = word0 & 0xfffu;
|
||||
inst.idxen = ((word0 >> 13u) & 1u) != 0;
|
||||
@@ -276,7 +267,7 @@ void DecodeMubuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
ApplyMemoryInfo(inst, info);
|
||||
SetRawWords(inst, code, word_index, 2);
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
MarkMemoryUnsupported(inst, Family::MUBUF, opcode, "MUBUF opcode is not implemented");
|
||||
SetUnsupported(inst, Family::MUBUF, opcode, "MUBUF opcode is not implemented");
|
||||
}
|
||||
|
||||
DecodeVectorGpr(vdata, inst.dst);
|
||||
@@ -299,7 +290,6 @@ void DecodeMtbuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
const uint32_t soffset = (word1 >> 24u) & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = 2;
|
||||
inst.offset = word0 & 0xfffu;
|
||||
inst.idxen = ((word0 >> 13u) & 1u) != 0;
|
||||
@@ -314,7 +304,7 @@ void DecodeMtbuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
ApplyMemoryInfo(inst, info);
|
||||
SetRawWords(inst, code, word_index, 2);
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
MarkMemoryUnsupported(inst, Family::MTBUF, opcode, "MTBUF opcode is not implemented");
|
||||
SetUnsupported(inst, Family::MTBUF, opcode, "MTBUF opcode is not implemented");
|
||||
}
|
||||
|
||||
DecodeVectorGpr(vdata, inst.dst);
|
||||
@@ -339,7 +329,6 @@ void DecodeFlat(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t addr = word1 & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = 2;
|
||||
inst.offset = seg == 0u ? (offset & 0x7ffu) : SignExtendU32(offset, 12u);
|
||||
inst.glc = ((word0 >> 16u) & 1u) != 0;
|
||||
@@ -356,7 +345,7 @@ void DecodeFlat(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
return;
|
||||
}
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
MarkMemoryUnsupported(inst, Family::FLAT, opcode, "FLAT opcode is not implemented");
|
||||
SetUnsupported(inst, Family::FLAT, opcode, "FLAT opcode is not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -384,7 +373,6 @@ void DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
const uint32_t addr = word1 & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = 2;
|
||||
inst.offset = offset0 | (offset1 << 8u);
|
||||
inst.gds = ((word0 >> 17u) & 1u) != 0u;
|
||||
@@ -394,7 +382,7 @@ void DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
ApplyMemoryInfo(inst, info);
|
||||
SetRawWords(inst, code, word_index, 2);
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
MarkMemoryUnsupported(inst, Family::DS, opcode, "DS opcode is not implemented");
|
||||
SetUnsupported(inst, Family::DS, opcode, "DS opcode is not implemented");
|
||||
}
|
||||
if (inst.opcode == Opcode::DS_SWIZZLE_B32 && inst.offset >= 0xe000u) {
|
||||
SetUnsupported(inst, Family::DS, opcode, "DS swizzle FFT mode is not implemented");
|
||||
|
||||
@@ -129,7 +129,6 @@ void DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t sdst = (word >> 16u) & 0x7fu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::SOP1;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(SOP1_OPS, opcode);
|
||||
@@ -169,7 +168,6 @@ void DecodeSop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t sdst = (word >> 16u) & 0x7fu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::SOP2;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(SOP2_OPS, opcode);
|
||||
@@ -194,7 +192,6 @@ void DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
: static_cast<int32_t>(static_cast<int16_t>(word & 0xffffu));
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::SOPK;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(SOPK_OPS, opcode);
|
||||
@@ -250,7 +247,6 @@ void DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t opcode = (word >> 16u) & 0x7fu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::SOPC;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(SOPC_OPS, opcode);
|
||||
@@ -272,7 +268,6 @@ void DecodeSopp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t simm = word & 0xffffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::SOPP;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(SOPP_OPS, opcode);
|
||||
@@ -288,8 +283,8 @@ void DecodeSopp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.opcode == Opcode::S_TTRACEDATA || inst.opcode == Opcode::S_INST_PREFETCH)
|
||||
? 1
|
||||
: 0;
|
||||
inst.branch_offset = static_cast<int32_t>(static_cast<int16_t>(simm)) * 4;
|
||||
inst.branch_target = pc + 4u + static_cast<uint32_t>(inst.branch_offset);
|
||||
const auto branch_offset = static_cast<int32_t>(static_cast<int16_t>(simm)) * 4;
|
||||
inst.branch_target = pc + 4u + static_cast<uint32_t>(branch_offset);
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
|
||||
@@ -47,7 +47,7 @@ void ApplyLiteral(Operand& operand, uint32_t literal) {
|
||||
|
||||
std::string RawWordsToString(const Instruction& inst) {
|
||||
std::string text;
|
||||
for (uint32_t i = 0; i < inst.raw_count; i++) {
|
||||
for (uint32_t i = 0; i < inst.word_count; i++) {
|
||||
if (i != 0) {
|
||||
text += " ";
|
||||
}
|
||||
@@ -234,8 +234,7 @@ void DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand) {
|
||||
if (code >= 240u && code <= 247u) {
|
||||
constexpr float values[] = {0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f, 4.0f, -4.0f};
|
||||
operand.kind = OperandKind::FloatInlineConstant;
|
||||
operand.float_val = values[code - 240u];
|
||||
operand.value = FloatBits(operand.float_val);
|
||||
operand.value = FloatBits(values[code - 240u]);
|
||||
return;
|
||||
}
|
||||
if (code >= 256u && code <= 511u) {
|
||||
@@ -253,8 +252,7 @@ void DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand) {
|
||||
case 239u: operand.kind = OperandKind::PopsExitingWaveId; return;
|
||||
case 248u:
|
||||
operand.kind = OperandKind::FloatInlineConstant;
|
||||
operand.float_val = 0.15915494309189535f;
|
||||
operand.value = FloatBits(operand.float_val);
|
||||
operand.value = FloatBits(0.15915494309189535f);
|
||||
return;
|
||||
case 251u: operand.kind = OperandKind::VccZ; return;
|
||||
case 252u: operand.kind = OperandKind::ExecZ; return;
|
||||
@@ -307,8 +305,7 @@ void ReadLiteralOperands(std::span<const uint32_t> code, uint32_t word_index, In
|
||||
void SetRawWords(Instruction& inst, std::span<const uint32_t> code, uint32_t word_index,
|
||||
uint32_t word_count) {
|
||||
inst.word_count = word_count;
|
||||
inst.raw_count = word_count;
|
||||
for (uint32_t i = 0; i < inst.raw_count; i++) {
|
||||
for (uint32_t i = 0; i < inst.word_count; i++) {
|
||||
inst.raw[i] = code[word_index + i];
|
||||
}
|
||||
}
|
||||
@@ -415,7 +412,9 @@ std::string OperandToString(const Operand& operand) {
|
||||
case OperandKind::IntegerInlineConstant:
|
||||
text = fmt::format("{}", operand.signed_val);
|
||||
break;
|
||||
case OperandKind::FloatInlineConstant: text = fmt::format("{:f}", operand.float_val); break;
|
||||
case OperandKind::FloatInlineConstant:
|
||||
text = fmt::format("{:f}", std::bit_cast<float>(operand.value));
|
||||
break;
|
||||
case OperandKind::Sgpr: text = fmt::format("s{}", operand.reg); break;
|
||||
case OperandKind::Vgpr: text = fmt::format("v{}", operand.reg); break;
|
||||
case OperandKind::VccLo: text = "vcc_lo"; break;
|
||||
|
||||
@@ -634,7 +634,6 @@ struct Operand {
|
||||
OperandKind kind = OperandKind::Unknown;
|
||||
uint32_t value = 0;
|
||||
int32_t signed_val = 0;
|
||||
float float_val = 0.0f;
|
||||
uint32_t reg = 0;
|
||||
uint32_t sdwa_sel = 6;
|
||||
// Native 16-bit destinations use the same selector fields internally but preserve the
|
||||
@@ -659,10 +658,8 @@ struct Operand {
|
||||
|
||||
struct Instruction {
|
||||
uint32_t pc = 0;
|
||||
uint32_t word = 0;
|
||||
uint32_t word_count = 1;
|
||||
uint32_t raw[MaxInstructionRawWords] = {};
|
||||
uint32_t raw_count = 1;
|
||||
Family family = Family::Unknown;
|
||||
uint32_t opcode_id = 0;
|
||||
Opcode opcode = Opcode::UNKNOWN;
|
||||
@@ -696,7 +693,6 @@ struct Instruction {
|
||||
bool idxen = false;
|
||||
bool offen = false;
|
||||
bool image_r128 = false;
|
||||
int32_t branch_offset = 0;
|
||||
uint32_t branch_target = 0;
|
||||
struct {
|
||||
uint32_t target = 0;
|
||||
|
||||
@@ -714,57 +714,7 @@ void DecodeVop1Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
|
||||
ReadLiteralOperands(code, word_index, inst);
|
||||
}
|
||||
|
||||
using Vop1ModifierDecodeFn = void (*)(uint32_t pc, std::span<const uint32_t> code,
|
||||
uint32_t word_index, uint32_t opcode, uint32_t vdst,
|
||||
Instruction& inst);
|
||||
|
||||
struct Vop1ModifierDecoder {
|
||||
uint32_t escape = 0;
|
||||
Vop1ModifierDecodeFn decode = nullptr;
|
||||
};
|
||||
|
||||
constexpr Vop1ModifierDecoder VOP1_MODIFIER_DECODERS[] = {
|
||||
{249u, DecodeVop1Sdwa},
|
||||
{250u, DecodeVop1Dpp},
|
||||
};
|
||||
|
||||
bool TryDecodeVop1Modifier(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
uint32_t src0, uint32_t opcode, uint32_t vdst, Instruction& inst) {
|
||||
for (const auto& decoder: VOP1_MODIFIER_DECODERS) {
|
||||
if (decoder.escape == src0) {
|
||||
decoder.decode(pc, code, word_index, opcode, vdst, inst);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsVop2FloatSourceOpcode(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Opcode::V_ADD_F32:
|
||||
case Opcode::V_SUB_F32:
|
||||
case Opcode::V_SUBREV_F32:
|
||||
case Opcode::V_MUL_F32:
|
||||
case Opcode::V_MIN_F32:
|
||||
case Opcode::V_MAX_F32:
|
||||
case Opcode::V_MAC_F32:
|
||||
case Opcode::V_MADMK_F32:
|
||||
case Opcode::V_MADAK_F32:
|
||||
case Opcode::V_CVT_PKRTZ_F16_F32:
|
||||
case Opcode::V_ADD_F16:
|
||||
case Opcode::V_SUB_F16:
|
||||
case Opcode::V_SUBREV_F16:
|
||||
case Opcode::V_MUL_F16:
|
||||
case Opcode::V_FMAC_F16:
|
||||
case Opcode::V_FMAMK_F16:
|
||||
case Opcode::V_FMAAK_F16:
|
||||
case Opcode::V_MAX_F16:
|
||||
case Opcode::V_MIN_F16: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsVop2FloatResultOpcode(Opcode opcode) {
|
||||
bool IsVop2FloatOpcode(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Opcode::V_ADD_F32:
|
||||
case Opcode::V_SUB_F32:
|
||||
@@ -975,12 +925,12 @@ bool ValidateVop2Sdwa(Instruction& inst, uint32_t opcode, const Vop2SdwaFields&
|
||||
SetUnsupported(inst, Family::VOP2, opcode, "VOP2 SDWA selector is invalid");
|
||||
return false;
|
||||
}
|
||||
if ((fields.clamp != 0u || fields.omod != 0u) && !IsVop2FloatResultOpcode(inst.opcode)) {
|
||||
if ((fields.clamp != 0u || fields.omod != 0u) && !IsVop2FloatOpcode(inst.opcode)) {
|
||||
SetUnsupported(inst, Family::VOP2, opcode, "VOP2 SDWA output modifiers are not supported");
|
||||
return false;
|
||||
}
|
||||
if (IsFullWidthVop2Sdwa(fields)) {
|
||||
if (!IsVop2FloatSourceOpcode(inst.opcode) && inst.opcode != Opcode::V_CNDMASK_B32 &&
|
||||
if (!IsVop2FloatOpcode(inst.opcode) && inst.opcode != Opcode::V_CNDMASK_B32 &&
|
||||
(fields.src0_neg != 0u || fields.src0_abs != 0u || fields.src1_neg != 0u ||
|
||||
fields.src1_abs != 0u)) {
|
||||
SetUnsupported(inst, Family::VOP2, opcode,
|
||||
@@ -1098,7 +1048,7 @@ void DecodeVop2Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
|
||||
inst.src1.negate_hi = inst.src1.negate;
|
||||
}
|
||||
|
||||
if (!IsVop2FloatSourceOpcode(inst.opcode) && !packed_fmac &&
|
||||
if (!IsVop2FloatOpcode(inst.opcode) && !packed_fmac &&
|
||||
(inst.src0.negate || inst.src0.absolute || inst.src1.negate || inst.src1.absolute)) {
|
||||
SetUnsupported(inst, Family::VOP2, opcode,
|
||||
"VOP2 DPP integer source modifiers are not supported");
|
||||
@@ -1107,32 +1057,6 @@ void DecodeVop2Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
|
||||
FinalizeVop2Instruction(code, word_index, inst);
|
||||
}
|
||||
|
||||
using Vop2ModifierDecodeFn = void (*)(uint32_t pc, std::span<const uint32_t> code,
|
||||
uint32_t word_index, uint32_t opcode, uint32_t vdst,
|
||||
uint32_t vsrc1, Instruction& inst);
|
||||
|
||||
struct Vop2ModifierDecoder {
|
||||
uint32_t escape = 0;
|
||||
Vop2ModifierDecodeFn decode = nullptr;
|
||||
};
|
||||
|
||||
constexpr Vop2ModifierDecoder VOP2_MODIFIER_DECODERS[] = {
|
||||
{249u, DecodeVop2Sdwa},
|
||||
{250u, DecodeVop2Dpp},
|
||||
};
|
||||
|
||||
bool TryDecodeVop2Modifier(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
uint32_t src0, uint32_t opcode, uint32_t vdst, uint32_t vsrc1,
|
||||
Instruction& inst) {
|
||||
for (const auto& decoder: VOP2_MODIFIER_DECODERS) {
|
||||
if (decoder.escape == src0) {
|
||||
decoder.decode(pc, code, word_index, opcode, vdst, vsrc1, inst);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct VopcSdwaFields {
|
||||
uint32_t src0 = 0;
|
||||
uint32_t sdst = 0;
|
||||
@@ -1534,7 +1458,6 @@ void DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t vsrc1 = (word >> 9u) & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::VOP2;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = LookupVop2Opcode(opcode);
|
||||
@@ -1544,8 +1467,10 @@ void DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
SetUnsupported(inst, Family::VOP2, opcode, "VOP2 opcode is not implemented");
|
||||
return;
|
||||
}
|
||||
if (TryDecodeVop2Modifier(pc, code, word_index, src0, opcode, vdst, vsrc1, inst)) {
|
||||
return;
|
||||
switch (src0) {
|
||||
case 249u: DecodeVop2Sdwa(pc, code, word_index, opcode, vdst, vsrc1, inst); return;
|
||||
case 250u: DecodeVop2Dpp(pc, code, word_index, opcode, vdst, vsrc1, inst); return;
|
||||
default: break;
|
||||
}
|
||||
|
||||
DecodeVectorGpr(vdst, inst.dst);
|
||||
@@ -1562,7 +1487,6 @@ void DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t vdst = (word >> 17u) & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::VOP1;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(VOP1_OPS, opcode);
|
||||
@@ -1577,8 +1501,10 @@ void DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.src_count = 0;
|
||||
return;
|
||||
}
|
||||
if (TryDecodeVop1Modifier(pc, code, word_index, src0, opcode, vdst, inst)) {
|
||||
return;
|
||||
switch (src0) {
|
||||
case 249u: DecodeVop1Sdwa(pc, code, word_index, opcode, vdst, inst); return;
|
||||
case 250u: DecodeVop1Dpp(pc, code, word_index, opcode, vdst, inst); return;
|
||||
default: break;
|
||||
}
|
||||
const bool scalar_dst = UsesScalarDestination(inst.opcode);
|
||||
if (scalar_dst) {
|
||||
@@ -1599,7 +1525,6 @@ void DecodeVopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t vsrc1 = (word >> 9u) & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::VOPC;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(VOPC_OPS, opcode);
|
||||
@@ -1638,7 +1563,6 @@ void DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t neg = (word1 >> 29u) & 0x7u;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.family = Family::VOP3;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = LookupVop3Opcode(opcode);
|
||||
@@ -1792,7 +1716,6 @@ void DecodeVop3p(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
const uint32_t neg = (word1 >> 29u) & 0x7u;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.family = Family::VOP3P;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(VOP3P_OPS, opcode);
|
||||
@@ -1842,7 +1765,6 @@ void DecodeVintrp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_ind
|
||||
const uint32_t vsrc = word & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.word_count = 1;
|
||||
inst.family = Family::VINTRP;
|
||||
inst.opcode_id = opcode;
|
||||
|
||||
@@ -33,7 +33,7 @@ void Translator::S_SAVEEXEC(const Decoder::Instruction& inst, IR::ValueOpcode op
|
||||
WriteMask(inst.dst, old, true);
|
||||
} else {
|
||||
WriteRawU32(inst.dst, ir.GetExecLo());
|
||||
if (current_wave_size == 64u) {
|
||||
if (program.wave_size == 64u) {
|
||||
const auto low_half =
|
||||
ir.ULessThan(IR::U32(ir.Emit(IR::ValueOpcode::LaneId)), IR::U32(IR::Value(32u)));
|
||||
result = IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, {low_half, result, old}));
|
||||
@@ -328,7 +328,7 @@ void Translator::V_READFIRSTLANE_B32(const Decoder::Instruction& inst) {
|
||||
}
|
||||
|
||||
void Translator::V_READLANE_B32(const Decoder::Instruction& inst) {
|
||||
const auto lane_mask = IR::U32(IR::Value(current_wave_size == 32u ? 31u : 63u));
|
||||
const auto lane_mask = IR::U32(IR::Value(program.wave_size == 32u ? 31u : 63u));
|
||||
const auto lane = ir.BitwiseAnd(ReadU32(inst.src1), lane_mask);
|
||||
WriteOperand(DestinationOperand(inst),
|
||||
ir.Emit(IR::ValueOpcode::ReadLane, {ReadU32(inst.src0), lane}));
|
||||
@@ -336,7 +336,7 @@ void Translator::V_READLANE_B32(const Decoder::Instruction& inst) {
|
||||
|
||||
void Translator::V_WRITELANE_B32(const Decoder::Instruction& inst) {
|
||||
EXIT_IF(inst.dst.kind != Decoder::OperandKind::Vgpr);
|
||||
const auto lane_mask = IR::U32(IR::Value(current_wave_size == 32u ? 31u : 63u));
|
||||
const auto lane_mask = IR::U32(IR::Value(program.wave_size == 32u ? 31u : 63u));
|
||||
const auto reg = static_cast<IR::VectorReg>(inst.dst.reg);
|
||||
const auto lane = ir.BitwiseAnd(ReadU32(inst.src1), lane_mask);
|
||||
const auto result = IR::U32(
|
||||
|
||||
@@ -89,10 +89,10 @@ void Translator::V_CVT_16_F16(const Decoder::Instruction& inst, bool signed_valu
|
||||
if (signed_value) {
|
||||
const auto converted =
|
||||
ConvertF32ToI32Saturated(value, -32768.0f, 32768.0f, 32767.0f, 0xffff8000u, 0x7fffu);
|
||||
WriteU16(DestinationOperand(inst), ir.BitwiseAnd(converted, IR::U32(IR::Value(0xffffu))));
|
||||
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(converted, IR::U32(IR::Value(0xffffu))));
|
||||
return;
|
||||
}
|
||||
WriteU16(DestinationOperand(inst),
|
||||
Write16Bits(DestinationOperand(inst),
|
||||
ConvertF32ToU32Saturated(value, 65536.0f, 65535.0f, 0xffffu));
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ bool Translator::Float16Unary(const Decoder::Instruction& inst, IR::ValueOpcode
|
||||
result = ApplyF32ResultModifiers(inst.dst, result);
|
||||
const auto bits = PackHalf2x16(result, IR::F32(IR::Value::F32(0.0f)));
|
||||
const auto invalid = IR::U32(IR::Value(inst.dst.clamp ? 0u : 0xfe00u));
|
||||
WriteU16(DestinationOperand(inst), ir.Select(negative, invalid, bits));
|
||||
Write16Bits(DestinationOperand(inst), ir.Select(negative, invalid, bits));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ bool Translator::Float16Trig(const Decoder::Instruction& inst, IR::ValueOpcode o
|
||||
result = ApplyF32ResultModifiers(inst.dst, result);
|
||||
const auto bits = PackHalf2x16(result, IR::F32(IR::Value::F32(0.0f)));
|
||||
const auto invalid = IR::U32(IR::Value(inst.dst.clamp ? 0u : 0xfe00u));
|
||||
WriteU16(DestinationOperand(inst), ir.Select(infinite, invalid, bits));
|
||||
Write16Bits(DestinationOperand(inst), ir.Select(infinite, invalid, bits));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ bool Translator::Float16Ternary(const Decoder::Instruction& inst, IR::ValueOpcod
|
||||
bool accumulator, bool mix) {
|
||||
std::array<IR::Value, 3> args;
|
||||
for (uint32_t index = 0; index < args.size(); index++) {
|
||||
const auto operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
|
||||
const auto& operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
|
||||
args[index] = mix ? IR::Value(ReadMixF32(operand)) : IR::Value(ReadF16AsF32(operand));
|
||||
}
|
||||
WriteF16(DestinationOperand(inst), IR::F32(ir.Emit(opcode, {args[0], args[1], args[2]})));
|
||||
@@ -128,7 +128,7 @@ bool Translator::FloatBinary(const Decoder::Instruction& inst, IR::ValueOpcode o
|
||||
bool reverse) {
|
||||
std::array<IR::Value, 2> args;
|
||||
for (uint32_t index = 0; index < args.size(); index++) {
|
||||
const auto operand = SourceAt(inst, reverse ? 1u - index : index);
|
||||
const auto& operand = SourceAt(inst, reverse ? 1u - index : index);
|
||||
args[index] = ReadOperand(operand, IR::ArgTypeOf(opcode, index));
|
||||
}
|
||||
WriteOperand(DestinationOperand(inst), ir.Emit(opcode, {args[0], args[1]}));
|
||||
@@ -139,7 +139,7 @@ bool Translator::FloatTernary(const Decoder::Instruction& inst, IR::ValueOpcode
|
||||
bool accumulator, bool mix) {
|
||||
std::array<IR::Value, 3> args;
|
||||
for (uint32_t index = 0; index < args.size(); index++) {
|
||||
const auto operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
|
||||
const auto& operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
|
||||
const auto type = IR::ArgTypeOf(opcode, index);
|
||||
args[index] = type == IR::Type::F32 && mix ? IR::Value(ReadMixF32(operand))
|
||||
: ReadOperand(operand, type);
|
||||
@@ -203,9 +203,6 @@ bool Translator::V_CUBEMA_F32(const Decoder::Instruction& inst) {
|
||||
}
|
||||
|
||||
bool Translator::FloatCube(const Decoder::Instruction& inst, uint32_t result_kind) {
|
||||
const auto select_f32 = [&](IR::U1 condition, IR::F32 true_value, IR::F32 false_value) {
|
||||
return IR::F32(ir.Emit(IR::ValueOpcode::SelectF32, {condition, true_value, false_value}));
|
||||
};
|
||||
const auto x = ReadMixF32(inst.src0);
|
||||
const auto y = ReadMixF32(inst.src1);
|
||||
const auto z = ReadMixF32(inst.src2);
|
||||
@@ -223,18 +220,18 @@ bool Translator::FloatCube(const Decoder::Instruction& inst, uint32_t result_kin
|
||||
const auto y_neg = IR::U1(ir.Emit(IR::ValueOpcode::FPOrdLessThan32, {y, IR::Value::F32(0.0f)}));
|
||||
const auto z_neg = IR::U1(ir.Emit(IR::ValueOpcode::FPOrdLessThan32, {z, IR::Value::F32(0.0f)}));
|
||||
const auto select_face = [&](IR::F32 x_value, IR::F32 y_value, IR::F32 z_value) {
|
||||
return select_f32(z_face, z_value, select_f32(y_face, y_value, x_value));
|
||||
return SelectF32(z_face, z_value, SelectF32(y_face, y_value, x_value));
|
||||
};
|
||||
IR::F32 result;
|
||||
switch (result_kind) {
|
||||
case 0u:
|
||||
result = select_face(
|
||||
select_f32(x_neg, IR::F32(IR::Value::F32(1.0f)), IR::F32(IR::Value::F32(0.0f))),
|
||||
select_f32(y_neg, IR::F32(IR::Value::F32(3.0f)), IR::F32(IR::Value::F32(2.0f))),
|
||||
select_f32(z_neg, IR::F32(IR::Value::F32(5.0f)), IR::F32(IR::Value::F32(4.0f))));
|
||||
SelectF32(x_neg, IR::F32(IR::Value::F32(1.0f)), IR::F32(IR::Value::F32(0.0f))),
|
||||
SelectF32(y_neg, IR::F32(IR::Value::F32(3.0f)), IR::F32(IR::Value::F32(2.0f))),
|
||||
SelectF32(z_neg, IR::F32(IR::Value::F32(5.0f)), IR::F32(IR::Value::F32(4.0f))));
|
||||
break;
|
||||
case 1u: result = select_face(select_f32(x_neg, z, nz), x, select_f32(z_neg, nx, x)); break;
|
||||
case 2u: result = select_face(ny, select_f32(y_neg, nz, z), ny); break;
|
||||
case 1u: result = select_face(SelectF32(x_neg, z, nz), x, SelectF32(z_neg, nx, x)); break;
|
||||
case 2u: result = select_face(ny, SelectF32(y_neg, nz, z), ny); break;
|
||||
case 3u: {
|
||||
const auto two = IR::F32(IR::Value::F32(2.0f));
|
||||
result = select_face(IR::F32(ir.Emit(IR::ValueOpcode::FPMul32, {x, two})),
|
||||
|
||||
@@ -9,7 +9,7 @@ bool Translator::Integer16Shift(const Decoder::Instruction& inst, IR::ValueOpcod
|
||||
const auto value = ReadU16AsU32(inst.src1, arithmetic);
|
||||
const auto count = ir.BitwiseAnd(ReadU16AsU32(inst.src0, false), IR::U32(IR::Value(15u)));
|
||||
const auto result = IR::U32(ir.Emit(opcode, {value, count}));
|
||||
WriteU16(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ bool Translator::Integer16Binary(const Decoder::Instruction& inst, IR::ValueOpco
|
||||
const auto lhs = ReadU16AsU32(inst.src0, sign);
|
||||
const auto rhs = ReadU16AsU32(inst.src1, sign);
|
||||
const auto result = IR::U32(ir.Emit(opcode, {lhs, rhs}));
|
||||
WriteU16(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ bool Translator::V_MED3_I16(const Decoder::Instruction& inst) {
|
||||
const auto result = IR::U32(ir.Emit(
|
||||
IR::ValueOpcode::SMedTri32, {ReadU16AsU32(inst.src0, true), ReadU16AsU32(inst.src1, true),
|
||||
ReadU16AsU32(inst.src2, true)}));
|
||||
WriteU16(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ bool Translator::SimpleInteger(const Decoder::Instruction& inst, IR::ValueOpcode
|
||||
std::array<IR::Value, 3> args;
|
||||
for (uint32_t index = 0; index < inst.src_count; index++) {
|
||||
const auto arg_type = IR::ArgTypeOf(opcode, index);
|
||||
const auto operand = SourceAt(inst, reverse && index < 2u ? 1u - index : index);
|
||||
const auto& operand = SourceAt(inst, reverse && index < 2u ? 1u - index : index);
|
||||
args[index] = ReadOperand(operand, arg_type == IR::Type::Void ? type : arg_type);
|
||||
if (mask_shift_count && index == 1u) {
|
||||
args[index] = ir.BitwiseAnd(IR::U32(args[index]), IR::U32(IR::Value(31u)));
|
||||
@@ -563,10 +563,8 @@ bool Translator::PackB16(const Decoder::Instruction& inst, bool high0, bool high
|
||||
: ReadU32(inst.src0);
|
||||
const auto hi = high1 ? ir.ShiftRightLogical(ReadU32(inst.src1), IR::U32(IR::Value(16u)))
|
||||
: ReadU32(inst.src1);
|
||||
const auto low_bits = ir.BitwiseAnd(lo, IR::U32(IR::Value(0xffffu)));
|
||||
const auto high_bits = ir.ShiftLeftLogical(ir.BitwiseAnd(hi, IR::U32(IR::Value(0xffffu))),
|
||||
IR::U32(IR::Value(16u)));
|
||||
WriteOperand(DestinationOperand(inst), ir.BitwiseOr(low_bits, high_bits));
|
||||
const auto result = PackU16Lanes(lo, hi);
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,16 +71,22 @@ ResourceKind FlatSegmentResourceKind(uint32_t segment) {
|
||||
}
|
||||
}
|
||||
|
||||
bool IsScalarAddressLoad(Decoder::Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Decoder::Opcode::S_LOAD_DWORD:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX2:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX4:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX8:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX16: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
ResourceKind MemoryKind(const Decoder::Instruction& decoded) {
|
||||
switch (decoded.family) {
|
||||
case Decoder::Family::SMEM:
|
||||
return decoded.opcode == Decoder::Opcode::S_LOAD_DWORD ||
|
||||
decoded.opcode == Decoder::Opcode::S_LOAD_DWORDX2 ||
|
||||
decoded.opcode == Decoder::Opcode::S_LOAD_DWORDX4 ||
|
||||
decoded.opcode == Decoder::Opcode::S_LOAD_DWORDX8 ||
|
||||
decoded.opcode == Decoder::Opcode::S_LOAD_DWORDX16
|
||||
? ResourceKind::ScalarAddress
|
||||
: ResourceKind::ScalarBuffer;
|
||||
return IsScalarAddressLoad(decoded.opcode) ? ResourceKind::ScalarAddress
|
||||
: ResourceKind::ScalarBuffer;
|
||||
case Decoder::Family::MUBUF:
|
||||
case Decoder::Family::MTBUF: return ResourceKind::Buffer;
|
||||
case Decoder::Family::FLAT: return FlatSegmentResourceKind(decoded.memory_segment);
|
||||
@@ -146,16 +152,6 @@ IR::MemoryInfo MemoryInfoFromDecoded(const Decoder::Instruction& decoded) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
bool IsScalarAddressLoad(Decoder::Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Decoder::Opcode::S_LOAD_DWORD:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX2:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX4:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX8:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX16: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsScalarBufferLoad(Decoder::Opcode opcode) {
|
||||
switch (opcode) {
|
||||
@@ -290,15 +286,15 @@ IR::Value Translator::GetAddressResource(IR::Value low, IR::Value high) {
|
||||
|
||||
Translator::AddressOperands Translator::ReadAddressOperands(const Decoder::Instruction& inst,
|
||||
uint32_t first_source) {
|
||||
const auto memory = MemoryInfoFromDecoded(inst);
|
||||
const auto kind = MemoryKind(inst);
|
||||
const auto low = ReadU32(MemorySourceAt(inst, first_source));
|
||||
const auto high_or_base = MemorySourceAt(inst, first_source + 1u);
|
||||
if (memory.kind == IR::ResourceKind::Scratch) {
|
||||
if (kind == IR::ResourceKind::Scratch) {
|
||||
const auto offset =
|
||||
high_or_base.kind != Decoder::OperandKind::Vgpr ? ReadU32(high_or_base) : low;
|
||||
return {ir.Emit(IR::ValueOpcode::GetScratchResource), offset, IR::Value(0u)};
|
||||
}
|
||||
if (memory.kind == IR::ResourceKind::Global &&
|
||||
if (kind == IR::ResourceKind::Global &&
|
||||
high_or_base.kind != Decoder::OperandKind::Vgpr) {
|
||||
const auto base_low = ReadU32(high_or_base);
|
||||
const auto base_high = ReadU32(OffsetOperand(high_or_base, 1u));
|
||||
@@ -329,18 +325,17 @@ IR::Value Translator::GetSamplerResource(const IR::MemoryInfo& memory) {
|
||||
|
||||
IR::Value Translator::MakeImageAddress(const Decoder::Instruction& inst,
|
||||
const Decoder::Operand& base) {
|
||||
const auto memory = MemoryInfoFromDecoded(inst);
|
||||
std::array<IR::Value, 13> components {};
|
||||
components.fill(IR::Value(0u));
|
||||
const auto count =
|
||||
Decoder::ImageAddressDwordCount(memory.image_sample_flags, memory.image_address_components);
|
||||
Decoder::ImageAddressDwordCount(inst.image_sample_flags, inst.image_address_components);
|
||||
EXIT_IF(count > components.size());
|
||||
const auto nsa_components =
|
||||
std::min(memory.image_nsa_dwords * 4u, Decoder::MaxImageNsaAddressComponents);
|
||||
std::min(inst.image_nsa_dwords * 4u, Decoder::MaxImageNsaAddressComponents);
|
||||
for (uint32_t index = 0; index < count; index++) {
|
||||
if (index != 0u && index - 1u < nsa_components) {
|
||||
components[index] =
|
||||
ir.GetVectorReg(static_cast<IR::VectorReg>(memory.image_nsa_addr[index - 1u]));
|
||||
ir.GetVectorReg(static_cast<IR::VectorReg>(inst.image_nsa_addr[index - 1u]));
|
||||
} else {
|
||||
components[index] = ReadRawU32(OffsetOperand(PlainOperand(base), index));
|
||||
}
|
||||
@@ -384,11 +379,10 @@ void Translator::WriteImageComponents(const Decoder::Operand& dst, IR::Value val
|
||||
|
||||
Translator::BufferAddress Translator::ReadBufferAddress(const Decoder::Instruction& inst,
|
||||
uint32_t first_source) {
|
||||
const auto memory = MemoryInfoFromDecoded(inst);
|
||||
uint32_t cursor = first_source;
|
||||
const auto next = [&]() { return ReadU32(MemorySourceAt(inst, cursor++)); };
|
||||
const auto index = memory.idxen ? next() : IR::U32(IR::Value(0u));
|
||||
const auto offset = memory.offen ? next() : IR::U32(IR::Value(0u));
|
||||
const auto index = inst.idxen ? next() : IR::U32(IR::Value(0u));
|
||||
const auto offset = inst.offen ? next() : IR::U32(IR::Value(0u));
|
||||
const auto soffset = next();
|
||||
return {index, offset, soffset};
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
|
||||
Decoder::Operand Translator::SourceAt(const Decoder::Instruction& inst, uint32_t index) {
|
||||
const Decoder::Operand& Translator::SourceAt(const Decoder::Instruction& inst, uint32_t index) {
|
||||
switch (index) {
|
||||
case 0: return inst.src0;
|
||||
case 1: return inst.src1;
|
||||
@@ -25,7 +25,7 @@ Decoder::Operand Translator::DestinationOperand(const Decoder::Instruction& inst
|
||||
return destination;
|
||||
}
|
||||
for (uint32_t index = 0; index < std::min(inst.src_count, 3u); index++) {
|
||||
const auto source = SourceAt(inst, index);
|
||||
const auto& source = SourceAt(inst, index);
|
||||
if (!source.dpp) {
|
||||
continue;
|
||||
}
|
||||
@@ -112,7 +112,7 @@ Decoder::Operand Translator::PlainOperand(const Decoder::Operand& operand) {
|
||||
std::array<IR::U32, 2> Translator::BallotMask(IR::U1 value) {
|
||||
const auto mask = ir.Emit(IR::ValueOpcode::Ballot, {value});
|
||||
return {ir.CompositeExtract(mask, 0),
|
||||
current_wave_size == 64u ? ir.CompositeExtract(mask, 1) : IR::U32(IR::Value(0u))};
|
||||
program.wave_size == 64u ? ir.CompositeExtract(mask, 1) : IR::U32(IR::Value(0u))};
|
||||
}
|
||||
|
||||
IR::U32 Translator::ReadRawU32(const Decoder::Operand& operand) {
|
||||
@@ -440,10 +440,6 @@ void Translator::WriteF16(const Decoder::Operand& operand, IR::F32 value) {
|
||||
Write16Bits(operand, bits);
|
||||
}
|
||||
|
||||
void Translator::WriteU16(const Decoder::Operand& operand, IR::U32 value) {
|
||||
Write16Bits(operand, value);
|
||||
}
|
||||
|
||||
IR::U32 Translator::ReadU32(const Decoder::Operand& operand) {
|
||||
return IR::U32(ReadOperand(operand, IR::Type::U32));
|
||||
}
|
||||
@@ -596,7 +592,7 @@ void Translator::WriteU32Pair(const Decoder::Operand& operand,
|
||||
|
||||
IR::U1 Translator::ThreadBit(const std::array<IR::U32, 2>& mask) {
|
||||
const auto lane = IR::U32(ir.Emit(IR::ValueOpcode::LaneId));
|
||||
const auto word = current_wave_size == 64u
|
||||
const auto word = program.wave_size == 64u
|
||||
? ir.Select(ir.ULessThan(lane, IR::U32(IR::Value(32u))), mask[0], mask[1])
|
||||
: mask[0];
|
||||
const auto bit = ir.BitwiseAnd(lane, IR::U32(IR::Value(31u)));
|
||||
@@ -604,10 +600,6 @@ IR::U1 Translator::ThreadBit(const std::array<IR::U32, 2>& mask) {
|
||||
IR::U32(IR::Value(0u)));
|
||||
}
|
||||
|
||||
IR::U1 Translator::ReadCondition(const Decoder::Operand& operand) {
|
||||
return IR::U1(ReadOperand(operand, IR::Type::U1));
|
||||
}
|
||||
|
||||
IR::U32 Translator::ConditionBit(const Decoder::Operand& operand) {
|
||||
return ir.Select(ReadMask(operand), IR::U32(IR::Value(1u)), IR::U32(IR::Value(0u)));
|
||||
}
|
||||
@@ -621,7 +613,7 @@ IR::U1 Translator::ReadMask(const Decoder::Operand& operand) {
|
||||
switch (operand.kind) {
|
||||
case Decoder::OperandKind::Sgpr: {
|
||||
const auto reg = static_cast<IR::ScalarReg>(operand.reg);
|
||||
const auto mask = current_wave_size == 64u
|
||||
const auto mask = program.wave_size == 64u
|
||||
? ReadU32Pair(operand)
|
||||
: std::array {ReadRawU32(operand), IR::U32(IR::Value(0u))};
|
||||
return IR::U1(ir.Emit(
|
||||
@@ -632,7 +624,7 @@ IR::U1 Translator::ReadMask(const Decoder::Operand& operand) {
|
||||
case Decoder::OperandKind::ExecHi: return ir.GetExec();
|
||||
case Decoder::OperandKind::VccLo:
|
||||
case Decoder::OperandKind::VccHi:
|
||||
return current_wave_size == 32u
|
||||
return program.wave_size == 32u
|
||||
? ThreadBit({ReadRawU32(operand), IR::U32(IR::Value(0u))})
|
||||
: ir.GetVcc();
|
||||
case Decoder::OperandKind::Scc: return ir.GetScc();
|
||||
@@ -682,7 +674,7 @@ std::array<IR::U32, 2> Translator::WriteMask(const Decoder::Operand& operand, IR
|
||||
}
|
||||
ir.SetScalarReg(reg, mask[0]);
|
||||
// A wave32 VALU mask destination must not overwrite the neighboring SGPR.
|
||||
if ((write_64 || current_wave_size == 64u) &&
|
||||
if ((write_64 || program.wave_size == 64u) &&
|
||||
IR::RegIndex(reg) + 1u < IR::NumScalarRegs) {
|
||||
const auto high = static_cast<IR::ScalarReg>(IR::RegIndex(reg) + 1u);
|
||||
ir.SetScalarReg(high, mask[1]);
|
||||
@@ -700,7 +692,7 @@ std::array<IR::U32, 2> Translator::WriteMask(const Decoder::Operand& operand, IR
|
||||
}
|
||||
case Decoder::OperandKind::VccLo:
|
||||
case Decoder::OperandKind::VccHi: {
|
||||
if (!write_64 && current_wave_size == 32u) {
|
||||
if (!write_64 && program.wave_size == 32u) {
|
||||
WriteRawU32(operand, mask[0]);
|
||||
return mask;
|
||||
}
|
||||
@@ -1117,7 +1109,7 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
|
||||
: u32(0);
|
||||
const auto vertex = entry_ir.IMul(local, step);
|
||||
const auto first = entry_ir.IAdd(vertex, parity);
|
||||
const auto second = mesh.InputPrimitiveSize() == 3u
|
||||
const auto second = mesh.InputPrimitiveSize() >= 2u
|
||||
? entry_ir.ISub(entry_ir.IAdd(vertex, u32(1)), parity)
|
||||
: u32(0);
|
||||
const auto third = mesh.InputPrimitiveSize() == 3u
|
||||
@@ -1198,7 +1190,7 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
|
||||
}
|
||||
for (const auto& cfg_block: cfg.blocks) {
|
||||
const auto typed_index = block_indices.at(cfg_block.id);
|
||||
Translator translator(result, result.blocks[typed_index], vector_limit, options.wave_size);
|
||||
Translator translator(result, result.blocks[typed_index], vector_limit);
|
||||
for (uint32_t index = cfg_block.inst_begin; index < cfg_block.inst_end; index++) {
|
||||
const auto& instruction = decoded.instructions[index];
|
||||
if (IsCodeTableLoad(cfg, instruction.pc)) {
|
||||
|
||||
@@ -9,9 +9,8 @@ namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
|
||||
class Translator {
|
||||
public:
|
||||
Translator(IR::Program& program, IR::Block* block, uint32_t vector_limit, uint32_t wave_size)
|
||||
: program(program), ir(block), current_vector_limit(vector_limit),
|
||||
current_wave_size(wave_size) {}
|
||||
Translator(IR::Program& program, IR::Block* block, uint32_t vector_limit)
|
||||
: program(program), ir(block), current_vector_limit(vector_limit) {}
|
||||
|
||||
void TranslateInstruction(const Decoder::Instruction& inst);
|
||||
void TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
|
||||
@@ -19,7 +18,7 @@ public:
|
||||
void AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo& info);
|
||||
|
||||
private:
|
||||
Decoder::Operand SourceAt(const Decoder::Instruction& inst, uint32_t index);
|
||||
const Decoder::Operand& SourceAt(const Decoder::Instruction& inst, uint32_t index);
|
||||
Decoder::Operand DestinationOperand(const Decoder::Instruction& inst);
|
||||
Decoder::Operand OffsetOperand(const Decoder::Operand& operand, uint32_t offset);
|
||||
Decoder::Operand ScalarDestinationOperand(const Decoder::Operand& operand, uint32_t offset);
|
||||
@@ -36,7 +35,6 @@ private:
|
||||
IR::U32 PackHalf2x16(IR::F32 low, IR::F32 high);
|
||||
void Write16Bits(const Decoder::Operand& operand, IR::U32 value);
|
||||
void WriteF16(const Decoder::Operand& operand, IR::F32 value);
|
||||
void WriteU16(const Decoder::Operand& operand, IR::U32 value);
|
||||
IR::U32 ReadU32(const Decoder::Operand& operand);
|
||||
std::array<IR::U32, 2> ReadU32Pair(const Decoder::Operand& operand);
|
||||
IR::U64 ReadU64(const Decoder::Operand& operand);
|
||||
@@ -49,7 +47,6 @@ private:
|
||||
IR::U32 ReadF16LaneBits(const Decoder::Operand& operand, bool high_lane);
|
||||
std::array<IR::U32, 2> ExtractU64(IR::U64 value);
|
||||
void WriteU32Pair(const Decoder::Operand& operand, const std::array<IR::U32, 2>& value);
|
||||
IR::U1 ReadCondition(const Decoder::Operand& operand);
|
||||
IR::U32 ConditionBit(const Decoder::Operand& operand);
|
||||
IR::U1 ReadMask(const Decoder::Operand& operand);
|
||||
IR::U1 ReadMaskValid(const Decoder::Operand& operand);
|
||||
@@ -258,7 +255,6 @@ private:
|
||||
Decoder::Opcode current_opcode = Decoder::Opcode::UNKNOWN;
|
||||
uint32_t current_pc = 0;
|
||||
uint32_t current_vector_limit = 1;
|
||||
uint32_t current_wave_size = 64;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
|
||||
@@ -189,28 +189,27 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
program.blocks.size() != program.block_storage.size()) {
|
||||
return Fail("value IR block storage is inconsistent");
|
||||
}
|
||||
std::unordered_set<const Block*> blocks;
|
||||
std::unordered_map<const Block*, size_t> block_indices;
|
||||
std::unordered_map<uint32_t, const Block*> blocks_by_id;
|
||||
std::unordered_set<const Inst*> instructions;
|
||||
std::unordered_map<const Inst*, size_t> instruction_positions;
|
||||
for (size_t block_index = 0; block_index < program.blocks.size(); block_index++) {
|
||||
const auto* block = program.blocks[block_index];
|
||||
if (block == nullptr || program.block_storage[block_index] == nullptr ||
|
||||
block != program.block_storage[block_index].get()) {
|
||||
return Fail("value IR block pointer is inconsistent");
|
||||
}
|
||||
if (!blocks.insert(block).second) {
|
||||
if (!block_indices.emplace(block, block_index).second) {
|
||||
return Fail("value IR block pointer is duplicated");
|
||||
}
|
||||
block_indices.emplace(block, block_index);
|
||||
if (program.block_info[block_index].id == UINT32_MAX) {
|
||||
return Fail("value IR block uses the reserved exit id");
|
||||
}
|
||||
if (!blocks_by_id.emplace(program.block_info[block_index].id, block).second) {
|
||||
return Fail("value IR block id is duplicated");
|
||||
}
|
||||
size_t position = 0;
|
||||
for (const auto& inst: *block) {
|
||||
if (!instructions.insert(&inst).second) {
|
||||
if (!instruction_positions.emplace(&inst, position++).second) {
|
||||
return Fail("value IR instruction is duplicated");
|
||||
}
|
||||
}
|
||||
@@ -223,7 +222,7 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
const auto* block = program.blocks[block_index];
|
||||
std::unordered_set<const Block*> predecessors;
|
||||
for (const auto* predecessor: block->ImmPredecessors()) {
|
||||
if (predecessor == nullptr || !blocks.contains(predecessor)) {
|
||||
if (predecessor == nullptr || !block_indices.contains(predecessor)) {
|
||||
return Fail("value IR block has a foreign predecessor");
|
||||
}
|
||||
if (!predecessors.insert(predecessor).second) {
|
||||
@@ -237,7 +236,7 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
|
||||
std::unordered_set<const Block*> successors;
|
||||
for (const auto* successor: block->ImmSuccessors()) {
|
||||
if (successor == nullptr || !blocks.contains(successor)) {
|
||||
if (successor == nullptr || !block_indices.contains(successor)) {
|
||||
return Fail("value IR block has a foreign successor");
|
||||
}
|
||||
if (!successors.insert(successor).second) {
|
||||
@@ -264,7 +263,7 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
return false;
|
||||
}
|
||||
const auto* definition = value.TryInstruction();
|
||||
return definition == nullptr || instructions.contains(definition);
|
||||
return definition == nullptr || instruction_positions.contains(definition);
|
||||
};
|
||||
switch (terminator.kind) {
|
||||
case CFG::TerminatorKind::Branch:
|
||||
@@ -338,7 +337,7 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
std::unordered_set<const Block*> incoming_blocks;
|
||||
for (size_t arg_index = 0; arg_index < inst.NumArgs(); arg_index++) {
|
||||
const auto* predecessor = inst.PhiBlock(arg_index);
|
||||
if (predecessor == nullptr || !blocks.contains(predecessor) ||
|
||||
if (predecessor == nullptr || !block_indices.contains(predecessor) ||
|
||||
!predecessors.contains(predecessor)) {
|
||||
return Fail("value IR Phi has a foreign or non-predecessor parent");
|
||||
}
|
||||
@@ -520,7 +519,7 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
TypeName(ArgTypeOf(inst.GetOpcode(), arg_index))));
|
||||
}
|
||||
if (const auto* definition = arg.TryInstruction();
|
||||
definition != nullptr && !instructions.contains(definition)) {
|
||||
definition != nullptr && !instruction_positions.contains(definition)) {
|
||||
return Fail("value IR argument has a foreign definition");
|
||||
}
|
||||
if (const auto* definition = arg.TryInstruction(); definition != nullptr) {
|
||||
@@ -582,13 +581,6 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<const Inst*, size_t> instruction_positions;
|
||||
for (const auto* block: program.blocks) {
|
||||
size_t position = 0;
|
||||
for (const auto& inst: *block) {
|
||||
instruction_positions.emplace(&inst, position++);
|
||||
}
|
||||
}
|
||||
const auto dominates = [&](const Block* definition, const Block* use) {
|
||||
return dominators[block_indices.at(use)][block_indices.at(definition)];
|
||||
};
|
||||
|
||||
@@ -415,7 +415,7 @@ struct BindingLayout {
|
||||
|
||||
struct ShaderInfo {
|
||||
static constexpr uint32_t MaxBuffers = 32;
|
||||
static constexpr uint32_t MaxImages = 32;
|
||||
static constexpr uint32_t MaxImages = 64;
|
||||
static constexpr uint32_t MaxSamplers = 32;
|
||||
static constexpr uint32_t MaxSampledPairs = 64;
|
||||
|
||||
|
||||
@@ -280,8 +280,7 @@ private:
|
||||
return finish(false);
|
||||
}
|
||||
}
|
||||
if (op == ValueOpcode::GetBufferResource || op == ValueOpcode::GetImageResource ||
|
||||
op == ValueOpcode::GetSamplerResource || op == ValueOpcode::GetAddressResource) {
|
||||
if (IsDescriptorHandle(op)) {
|
||||
size_t expected = 4u;
|
||||
if (op == ValueOpcode::GetImageResource) {
|
||||
expected = 8u;
|
||||
|
||||
@@ -123,46 +123,6 @@ struct DefTable {
|
||||
std::unordered_map<uint32_t, ValueMap> goto_variables;
|
||||
};
|
||||
|
||||
ValueOpcode UndefOpcode(ScalarReg) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
ValueOpcode UndefOpcode(ThreadBitScalarReg) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(ScalarMaskTag) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(VectorReg) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
ValueOpcode UndefOpcode(GotoVariable) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(SccTag) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(ExecTag) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(ExecLoTag) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
ValueOpcode UndefOpcode(ExecHiTag) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
ValueOpcode UndefOpcode(VccTag) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(VccLoTag) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
ValueOpcode UndefOpcode(VccHiTag) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
ValueOpcode UndefOpcode(M0Tag) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
|
||||
Value InitialValue(ScalarReg) {
|
||||
return Value(0u);
|
||||
}
|
||||
@@ -248,7 +208,7 @@ public:
|
||||
state.result = def;
|
||||
} else if (!block->IsSsaSealed()) {
|
||||
auto& phi = *block->PrependNewInst(block->begin(), ValueOpcode::Phi);
|
||||
phi.SetFlags(TypeOf(UndefOpcode(variable)));
|
||||
phi.SetFlags(InitialValue(variable).GetType());
|
||||
incomplete_phis[block][Variable(variable)] = φ
|
||||
state.result = Value(&phi);
|
||||
} else if (const auto predecessors = block->ImmPredecessors();
|
||||
@@ -258,7 +218,7 @@ public:
|
||||
break;
|
||||
} else {
|
||||
auto& phi = *block->PrependNewInst(block->begin(), ValueOpcode::Phi);
|
||||
phi.SetFlags(TypeOf(UndefOpcode(variable)));
|
||||
phi.SetFlags(InitialValue(variable).GetType());
|
||||
Write(variable, block, Value(&phi));
|
||||
state.phi = φ
|
||||
prepare_phi();
|
||||
|
||||
@@ -593,7 +593,6 @@ static void ShaderGetStaticInputInfoPS(
|
||||
}
|
||||
ps_info.ps_pos_x = (active_inputs & 0x00000100u) != 0;
|
||||
ps_info.ps_pos_y = (active_inputs & 0x00000200u) != 0;
|
||||
ps_info.ps_pos_xy = ps_info.ps_pos_x && ps_info.ps_pos_y;
|
||||
ps_info.ps_pos_z = (active_inputs & 0x00000400u) != 0;
|
||||
ps_info.ps_pos_w = (active_inputs & 0x00000800u) != 0;
|
||||
ps_info.ps_front_face = (active_inputs & 0x00001000u) != 0;
|
||||
@@ -756,19 +755,9 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context&
|
||||
}
|
||||
return params;
|
||||
}
|
||||
EXIT_IF(regs.gs_regs.data_addr == 0);
|
||||
const auto back = ShaderGetMappedData(regs.gs_regs.data_addr, "ShaderGetInputInfoGS():");
|
||||
const auto back_params =
|
||||
GetShaderParams(regs.gs_regs.data_addr, "ShaderRecompiler GS",
|
||||
GetDeclaredShaderHash(regs.gs_regs.data_addr), {}, back);
|
||||
params.back_code = back_params.code;
|
||||
// Merged shaders receive the GS-back user-data pointer in s0:s1, followed
|
||||
// by the ordinary user SGPRs at s8. Keep both in the runtime register snapshot.
|
||||
// NGG user SGPRs start at s8; a separately compiled GS back half also receives
|
||||
// its user-data pointer in s0:s1.
|
||||
params.user_data.insert(params.user_data.begin(), 8u, 0u);
|
||||
params.user_data[0] = static_cast<uint32_t>(regs.gs_regs.user_data_addr);
|
||||
params.user_data[1] = static_cast<uint32_t>(regs.gs_regs.user_data_addr >> 32u);
|
||||
const uint64_t hashes[] = {params.hash, back_params.hash};
|
||||
params.hash = XXH3_64bits(hashes, sizeof(hashes));
|
||||
info = {};
|
||||
info.pa_cl_vs_out_cntl = sh.m_paClVsOutCntl;
|
||||
auto& mesh = info.mesh;
|
||||
@@ -777,11 +766,25 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context&
|
||||
mesh.max_vertices = sh.m_geMaxOutputPerSubgroup;
|
||||
mesh.provoking_vertex = context.GetModeControl().provoking_vtx_last ? 2u : 0u;
|
||||
mesh.lds_size_dwords = static_cast<uint32_t>(regs.gs_regs.rsrc2.lds_size) * 128u;
|
||||
mesh.scratch_size_dwords = std::max(data.scratch_size_dwords, back.scratch_size_dwords);
|
||||
mesh.scratch_size_dwords = data.scratch_size_dwords;
|
||||
if (data.type == Prospero::ShaderBinaryType::kGsFront) {
|
||||
EXIT_IF(regs.gs_regs.data_addr == 0);
|
||||
const auto back = ShaderGetMappedData(regs.gs_regs.data_addr, "ShaderGetInputInfoGS():");
|
||||
const auto back_params =
|
||||
GetShaderParams(regs.gs_regs.data_addr, "ShaderRecompiler GS",
|
||||
GetDeclaredShaderHash(regs.gs_regs.data_addr), {}, back);
|
||||
params.back_code = back_params.code;
|
||||
params.user_data[0] = static_cast<uint32_t>(regs.gs_regs.user_data_addr);
|
||||
params.user_data[1] = static_cast<uint32_t>(regs.gs_regs.user_data_addr >> 32u);
|
||||
const uint64_t hashes[] = {params.hash, back_params.hash};
|
||||
params.hash = XXH3_64bits(hashes, sizeof(hashes));
|
||||
mesh.scratch_size_dwords = std::max(mesh.scratch_size_dwords, back.scratch_size_dwords);
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(regs.gs_regs.rsrc1.gs_vgpr_component_count != 3u ||
|
||||
regs.gs_regs.rsrc2.es_vgpr_component_count != 3u);
|
||||
const auto& group = user_config.GetGeControl();
|
||||
if ((user_config.GetPrimType() != Prospero::PrimitiveType::kPointList &&
|
||||
user_config.GetPrimType() != Prospero::PrimitiveType::kLineList &&
|
||||
user_config.GetPrimType() != Prospero::PrimitiveType::kTriStrip &&
|
||||
user_config.GetPrimType() != Prospero::PrimitiveType::kTriList) ||
|
||||
sh.m_vgtGsOutPrimType != 2u || sh.m_vgtGsMaxVertOut < 3u ||
|
||||
|
||||
@@ -80,10 +80,15 @@ struct ShaderMeshInputInfo: ShaderWorkgroupInputInfo {
|
||||
uint32_t provoking_vertex = 0;
|
||||
|
||||
[[nodiscard]] constexpr uint32_t InputPrimitiveSize() const {
|
||||
return input_primitive == static_cast<uint32_t>(Prospero::PrimitiveType::kPointList) ? 1u : 3u;
|
||||
switch (static_cast<Prospero::PrimitiveType>(input_primitive)) {
|
||||
case Prospero::PrimitiveType::kPointList: return 1u;
|
||||
case Prospero::PrimitiveType::kLineList: return 2u;
|
||||
default: return 3u;
|
||||
}
|
||||
}
|
||||
[[nodiscard]] constexpr uint32_t InputPrimitiveStep() const {
|
||||
return input_primitive == static_cast<uint32_t>(Prospero::PrimitiveType::kTriList) ? 3u : 1u;
|
||||
return input_primitive == static_cast<uint32_t>(Prospero::PrimitiveType::kTriStrip)
|
||||
? 1u : InputPrimitiveSize();
|
||||
}
|
||||
[[nodiscard]] constexpr uint32_t InputPrimitiveCount(uint32_t vertices) const {
|
||||
const auto size = InputPrimitiveSize();
|
||||
@@ -102,7 +107,6 @@ struct ShaderVertexInputInfo {
|
||||
ShaderVertexInputBuffer buffers[RES_MAX];
|
||||
ShaderStageRuntime stage;
|
||||
int resources_num = 0;
|
||||
int fetch_shader_reg = 0;
|
||||
int fetch_attrib_reg = 0;
|
||||
int fetch_buffer_reg = 0;
|
||||
int buffers_num = 0;
|
||||
@@ -135,7 +139,6 @@ struct ShaderPixelInputInfo {
|
||||
uint32_t scratch_size_dwords = 0;
|
||||
bool ps_pos_x = false;
|
||||
bool ps_pos_y = false;
|
||||
bool ps_pos_xy = false;
|
||||
bool ps_pos_z = false;
|
||||
bool ps_pos_w = false;
|
||||
bool ps_front_face = false;
|
||||
@@ -259,6 +262,7 @@ struct Shader {
|
||||
};
|
||||
|
||||
struct ShaderMappedData {
|
||||
Prospero::ShaderBinaryType type {};
|
||||
ShaderUserData* user_data = nullptr;
|
||||
ShaderSemantic* input_semantics = nullptr;
|
||||
uint32_t num_input_semantics = 0;
|
||||
|
||||
@@ -26,9 +26,12 @@
|
||||
#include <QToolButton>
|
||||
#include <QTreeView>
|
||||
#include <QVBoxLayout>
|
||||
#include <QtAlgorithms>
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
#include <QVulkanInstance>
|
||||
#include <QVulkanWindow>
|
||||
#include <QtAlgorithms>
|
||||
#endif
|
||||
|
||||
#include "ui_configuration_edit_dialog.h"
|
||||
|
||||
@@ -168,6 +171,8 @@ void ConfigurationEditDialog::Init(const Configuration& info) {
|
||||
ListInit(m_ui->comboBox_present_mode, info.present_mode);
|
||||
m_ui->comboBox_gpu->clear();
|
||||
m_ui->comboBox_gpu->addItem(tr("Auto"));
|
||||
// Keep Auto when Qt is built without Vulkan support.
|
||||
#if QT_CONFIG(vulkan)
|
||||
#if defined(__APPLE__)
|
||||
if (!qEnvironmentVariableIsSet("QT_VULKAN_LIB")) {
|
||||
const auto base = QCoreApplication::applicationDirPath();
|
||||
@@ -192,6 +197,7 @@ void ConfigurationEditDialog::Init(const Configuration& info) {
|
||||
m_ui->comboBox_gpu->addItem(QString::fromUtf8(device.deviceName));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
m_ui->comboBox_gpu->setCurrentIndex(
|
||||
info.gpu_index >= 0 && info.gpu_index < m_ui->comboBox_gpu->count() - 1 ? info.gpu_index + 1
|
||||
: 0);
|
||||
|
||||
@@ -662,6 +662,7 @@ int KYTY_SYSV_ABI AgcCreateShader(Shader** dst, void* header, const volatile voi
|
||||
LOGF("\t base = 0x%016" PRIx64 "\n", base);
|
||||
|
||||
ShaderMappedData map;
|
||||
map.type = static_cast<Prospero::ShaderBinaryType>(h->type);
|
||||
map.user_data = h->user_data;
|
||||
map.input_semantics = h->input_semantics;
|
||||
map.num_input_semantics = h->num_input_semantics;
|
||||
|
||||
@@ -2112,6 +2112,33 @@ int KYTY_SYSV_ABI KernelSyncOnAddressWake(volatile void* address, int32_t count)
|
||||
return LibKernel::SyncOnAddress::Wake(address, count);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI UmtxOp(volatile void* address, int operation, uint64_t value,
|
||||
void* uaddr, const void* timeout) {
|
||||
constexpr int UMTX_OP_WAIT = 2;
|
||||
constexpr int UMTX_OP_WAKE = 3;
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(uaddr != nullptr);
|
||||
|
||||
switch (operation) {
|
||||
case UMTX_OP_WAIT:
|
||||
if (timeout != nullptr) {
|
||||
LibKernel::KernelTimespec duration {};
|
||||
std::memcpy(&duration, timeout, sizeof(duration));
|
||||
if (duration.tv_sec < 0 || duration.tv_nsec < 0 || duration.tv_nsec >= 1000000000) {
|
||||
*GetErrorAddr() = POSIX_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
EXIT("Valid _umtx_op timed waits are not implemented\n");
|
||||
}
|
||||
return POSIX_CALL(LibKernel::SyncOnAddress::Wait64(
|
||||
static_cast<volatile uint64_t*>(address), value, nullptr,
|
||||
LibKernel::KernelDispatchPendingSignalForCurrentThread));
|
||||
case UMTX_OP_WAKE:
|
||||
return POSIX_CALL(LibKernel::SyncOnAddress::Wake(address, static_cast<int32_t>(value)));
|
||||
default: EXIT("Unsupported _umtx_op operation: %d\n", operation);
|
||||
}
|
||||
}
|
||||
|
||||
LIB_DEFINE(InitLibKernel_1_Posix) {
|
||||
LIB_FUNC("k+AXqu2-eBc", getpagesize);
|
||||
LIB_FUNC("lLMT9vJAck0", clock_gettime);
|
||||
@@ -3310,6 +3337,7 @@ static void AddLibkernelUnityFunc(Loader::SymbolDatabase* s, const char* nid, ui
|
||||
}
|
||||
|
||||
LIB_DEFINE(InitLibKernel_1) {
|
||||
LIB_FUNC("04AjkP0jO9U", Posix::UmtxOp);
|
||||
InitLibKernel_1_FS(s);
|
||||
InitLibKernel_1_Mem(s);
|
||||
InitLibKernel_1_Equeue(s);
|
||||
|
||||
@@ -117,8 +117,7 @@ public:
|
||||
[[nodiscard]] Result DecodeInput(const Input& input, const FrameBuffer& frame_buffer,
|
||||
Output* output) {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
*output = {};
|
||||
m_draining = false;
|
||||
*output = {};
|
||||
|
||||
AVPacket* packet = av_packet_alloc();
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
@@ -200,18 +199,7 @@ public:
|
||||
return Result::ApiFail;
|
||||
}
|
||||
|
||||
if (!m_draining) {
|
||||
const int send_result = avcodec_send_packet(m_codec, nullptr);
|
||||
if (send_result == 0 || send_result == AVERROR_EOF) {
|
||||
m_draining = true;
|
||||
} else if (send_result != AVERROR(EAGAIN)) {
|
||||
LOGF("Videodec2: flushing decoder failed: %s (%d)\n", AvErrorString(send_result),
|
||||
send_result);
|
||||
av_frame_free(&frame);
|
||||
return Result::ApiFail;
|
||||
}
|
||||
}
|
||||
|
||||
// Guest Flush collects available pictures between AUs while retaining reference frames.
|
||||
const int receive_result = avcodec_receive_frame(m_codec, frame);
|
||||
if (receive_result == AVERROR(EAGAIN) || receive_result == AVERROR_EOF) {
|
||||
av_frame_free(&frame);
|
||||
@@ -232,7 +220,6 @@ public:
|
||||
void ResetDecoder() {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
avcodec_flush_buffers(m_codec);
|
||||
m_draining = false;
|
||||
ClearPictureMetadata();
|
||||
}
|
||||
|
||||
@@ -357,7 +344,6 @@ private:
|
||||
Config m_config;
|
||||
AVCodecContext* m_codec = nullptr;
|
||||
SwsContext* m_sws = nullptr;
|
||||
bool m_draining = false;
|
||||
std::mutex m_mutex;
|
||||
std::unordered_set<void*> m_picture_buffers;
|
||||
};
|
||||
|
||||
@@ -410,7 +410,7 @@ struct RenderExecutorTestAccess {
|
||||
std::vector<vk::DescriptorSetLayoutBinding> layout_bindings;
|
||||
bool compute = false;
|
||||
for (const auto *prepared : stages) {
|
||||
const auto &program = *prepared->program;
|
||||
const auto &program = *prepared->runtime->program;
|
||||
compute |= program.stage == ShaderType::Compute;
|
||||
const auto shader_stage = program.stage == ShaderType::Vertex
|
||||
? vk::ShaderStageFlagBits::eVertex
|
||||
@@ -460,7 +460,7 @@ struct RenderExecutorTestAccess {
|
||||
PreparedBindings &bindings) {
|
||||
std::array<PreparedBindings *, 1> stages{&bindings};
|
||||
auto pipeline = CreateDescriptorPipeline(executor, stages);
|
||||
const auto bind_point = bindings.program->stage == ShaderType::Compute
|
||||
const auto bind_point = bindings.runtime->program->stage == ShaderType::Compute
|
||||
? vk::PipelineBindPoint::eCompute
|
||||
: vk::PipelineBindPoint::eGraphics;
|
||||
executor.CommitBindings(buffer, bind_point, pipeline, stages);
|
||||
@@ -498,17 +498,15 @@ struct RenderExecutorTestAccess {
|
||||
}
|
||||
|
||||
static void ResolveRenderDepthTarget(RenderExecutor &executor,
|
||||
uint64_t submit_id,
|
||||
CommandBuffer &buffer,
|
||||
RenderDepthInfo &depth) {
|
||||
executor.ResolveRenderDepthTarget(submit_id, buffer, depth);
|
||||
executor.ResolveRenderDepthTarget(buffer, depth);
|
||||
}
|
||||
|
||||
static void ResolveRenderColorTarget(RenderExecutor &executor,
|
||||
uint64_t submit_id,
|
||||
CommandBuffer &buffer,
|
||||
RenderColorInfo &color, uint32_t slot) {
|
||||
executor.ResolveRenderColorTarget(submit_id, buffer, color, 0, slot);
|
||||
executor.ResolveRenderColorTarget(buffer, color, 0, slot);
|
||||
}
|
||||
|
||||
static void BindRenderTarget(RenderExecutor &executor, ImageId id) {
|
||||
@@ -1764,6 +1762,73 @@ public:
|
||||
return Renderer();
|
||||
}
|
||||
|
||||
void CheckHostImageAllocation() {
|
||||
constexpr const char *name = "HostImageAllocation";
|
||||
auto &graphics = RuntimeContext();
|
||||
VulkanImage image;
|
||||
vk::ImageCreateInfo create{};
|
||||
create.flags = vk::ImageCreateFlagBits::eMutableFormat;
|
||||
create.imageType = vk::ImageType::e2D;
|
||||
create.format = vk::Format::eR8G8B8A8Unorm;
|
||||
create.extent = {16, 8, 1};
|
||||
create.mipLevels = 3;
|
||||
create.arrayLayers = 4;
|
||||
create.samples = vk::SampleCountFlagBits::e1;
|
||||
create.tiling = vk::ImageTiling::eOptimal;
|
||||
create.usage = vk::ImageUsageFlagBits::eTransferSrc |
|
||||
vk::ImageUsageFlagBits::eTransferDst |
|
||||
vk::ImageUsageFlagBits::eSampled;
|
||||
create.initialLayout = vk::ImageLayout::eUndefined;
|
||||
|
||||
for (uint32_t pass = 0; pass < 2; ++pass) {
|
||||
Require(name, "create", graphics.CreateImage(create, image),
|
||||
"could not allocate native image");
|
||||
Require(name, "allocation metadata",
|
||||
image.image != nullptr && image.allocation != nullptr &&
|
||||
image.format == create.format &&
|
||||
image.image_type == create.imageType &&
|
||||
image.extent == create.extent &&
|
||||
image.layers == create.arrayLayers &&
|
||||
image.mip_levels == create.mipLevels &&
|
||||
image.samples == static_cast<uint32_t>(create.samples) &&
|
||||
image.usage == create.usage && image.flags == create.flags,
|
||||
"native image metadata did not follow its creation descriptor");
|
||||
Require(name, "fresh synchronization state",
|
||||
image.state.layout == create.initialLayout &&
|
||||
image.state.access_mask == vk::AccessFlags2{} &&
|
||||
image.state.pl_stage == vk::PipelineStageFlagBits2::eAllCommands &&
|
||||
image.subresource_states.empty(),
|
||||
"new allocation retained synchronization state from an old image");
|
||||
VkMemoryPropertyFlags properties{};
|
||||
vmaGetAllocationMemoryProperties(graphics.allocator, image.allocation,
|
||||
&properties);
|
||||
Require(name, "allocation policy",
|
||||
(properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0,
|
||||
"native image lost its device-local memory requirement");
|
||||
|
||||
// A reused presentation image may still describe its previous recording.
|
||||
// No commands use this fixture; seed that bookkeeping before retirement.
|
||||
image.state = {vk::PipelineStageFlagBits2::eTransfer,
|
||||
vk::AccessFlagBits2::eTransferWrite,
|
||||
vk::ImageLayout::eTransferDstOptimal};
|
||||
image.subresource_states.assign(image.layers * image.mip_levels, image.state);
|
||||
graphics.DeleteImage(image);
|
||||
Require(name, "retirement",
|
||||
image.image == nullptr && image.allocation == nullptr,
|
||||
"retired native image retained an allocation handle");
|
||||
|
||||
create.flags = vk::ImageCreateFlagBits::e2DArrayCompatible;
|
||||
create.imageType = vk::ImageType::e3D;
|
||||
create.format = vk::Format::eR32Uint;
|
||||
create.extent = {8, 4, 2};
|
||||
create.mipLevels = 2;
|
||||
create.arrayLayers = 1;
|
||||
create.usage = vk::ImageUsageFlagBits::eTransferSrc |
|
||||
vk::ImageUsageFlagBits::eTransferDst;
|
||||
}
|
||||
std::printf("[host] %-32s ok\n", name);
|
||||
}
|
||||
|
||||
void CheckDescriptorHeapLargeSet() {
|
||||
EnsureRuntimeContext();
|
||||
std::array<vk::DescriptorSetLayoutBinding, 3> bindings{};
|
||||
@@ -1848,15 +1913,12 @@ public:
|
||||
pixel_program.bindings.push_data_start_dword = 0;
|
||||
pixel_program.bindings.user_data_registers = {0, 1};
|
||||
pixel_program.bindings.memory_offset_dword = 2;
|
||||
ShaderRecompiler::IR::ResourceSnapshot snapshot{};
|
||||
PreparedBindings vertex{};
|
||||
vertex.program = &vertex_program;
|
||||
vertex.snapshot = &snapshot;
|
||||
vertex.shader_data = {0x11111111u, 0x22222222u};
|
||||
PreparedBindings pixel{};
|
||||
pixel.program = &pixel_program;
|
||||
pixel.snapshot = &snapshot;
|
||||
pixel.shader_data = {0x33333333u, 0x44444444u};
|
||||
ShaderStageRuntime vertex_runtime{.program = &vertex_program};
|
||||
vertex_runtime.resources.user_data = {0x11111111u, 0x22222222u};
|
||||
ShaderStageRuntime pixel_runtime{.program = &pixel_program};
|
||||
pixel_runtime.resources.user_data = {0x33333333u, 0x44444444u};
|
||||
auto vertex = context.GetRenderExecutor().PrepareBindings(vertex_runtime);
|
||||
auto pixel = context.GetRenderExecutor().PrepareBindings(pixel_runtime);
|
||||
|
||||
const auto pipeline = RenderExecutorTestAccess::CommitBindings(
|
||||
context.GetRenderExecutor(), scheduler.Current(), vertex, pixel);
|
||||
@@ -7853,7 +7915,7 @@ public:
|
||||
|
||||
RenderColorInfo color{};
|
||||
RenderExecutorTestAccess::ResolveRenderColorTarget(
|
||||
executor, 1, scheduler.Current(), color, 0);
|
||||
executor, scheduler.Current(), color, 0);
|
||||
const auto attachment =
|
||||
texture_cache.FindRenderTarget(color.image_id, color.desc);
|
||||
const auto &image = texture_cache.GetImage(color.image_id);
|
||||
@@ -7955,7 +8017,7 @@ public:
|
||||
|
||||
RenderColorInfo color{};
|
||||
RenderExecutorTestAccess::ResolveRenderColorTarget(
|
||||
executor, 1, scheduler.Current(), color, 0);
|
||||
executor, scheduler.Current(), color, 0);
|
||||
const auto attachment =
|
||||
texture_cache.FindRenderTarget(color.image_id, color.desc);
|
||||
const auto &image = texture_cache.GetImage(color.image_id);
|
||||
@@ -7988,7 +8050,7 @@ public:
|
||||
0, {.base_array_slice_index = 7, .last_array_slice_index = 7});
|
||||
RenderColorInfo sliced_color{};
|
||||
RenderExecutorTestAccess::ResolveRenderColorTarget(
|
||||
executor, 2, scheduler.Current(), sliced_color, 0);
|
||||
executor, scheduler.Current(), sliced_color, 0);
|
||||
RenderDepthInfo no_depth{};
|
||||
const auto sliced_rendering =
|
||||
RenderExecutorTestAccess::AcquireRenderTargets(
|
||||
@@ -8074,7 +8136,7 @@ public:
|
||||
.last_array_slice_index = 32});
|
||||
RenderColorInfo volume_color{};
|
||||
RenderExecutorTestAccess::ResolveRenderColorTarget(
|
||||
executor, 3, scheduler.Current(), volume_color, 0);
|
||||
executor, scheduler.Current(), volume_color, 0);
|
||||
const auto volume_rendering =
|
||||
RenderExecutorTestAccess::AcquireRenderTargets(
|
||||
executor, scheduler.Current(), &volume_color, 1, no_depth);
|
||||
@@ -8210,7 +8272,7 @@ public:
|
||||
|
||||
RenderColorInfo color{};
|
||||
RenderExecutorTestAccess::ResolveRenderColorTarget(
|
||||
executor, 1, scheduler.Current(), color, 0);
|
||||
executor, scheduler.Current(), color, 0);
|
||||
RenderDepthInfo no_depth{};
|
||||
const auto rendering = RenderExecutorTestAccess::AcquireRenderTargets(
|
||||
executor, scheduler.Current(), &color, 1, no_depth);
|
||||
@@ -8603,7 +8665,7 @@ public:
|
||||
|
||||
RenderColorInfo color{};
|
||||
RenderExecutorTestAccess::ResolveRenderColorTarget(
|
||||
executor, 1, scheduler.Current(), color, 0);
|
||||
executor, scheduler.Current(), color, 0);
|
||||
const auto attachment =
|
||||
texture_cache.FindRenderTarget(color.image_id, color.desc);
|
||||
const auto &image = texture_cache.GetImage(color.image_id);
|
||||
@@ -8713,7 +8775,7 @@ public:
|
||||
|
||||
RenderColorInfo color{};
|
||||
RenderExecutorTestAccess::ResolveRenderColorTarget(
|
||||
executor, 1, scheduler.Current(), color, 0);
|
||||
executor, scheduler.Current(), color, 0);
|
||||
const auto attachment =
|
||||
texture_cache.FindRenderTarget(color.image_id, color.desc);
|
||||
const auto &image = texture_cache.GetImage(color.image_id);
|
||||
@@ -9120,8 +9182,8 @@ public:
|
||||
mipped_storage_descriptor,
|
||||
overwide_mipped_storage_descriptor,
|
||||
overwide_mipped_storage_descriptor};
|
||||
mipped_prepared.program = &mipped_program;
|
||||
mipped_prepared.snapshot = &mipped_snapshot;
|
||||
ShaderStageRuntime mipped_runtime{&mipped_program, std::move(mipped_snapshot)};
|
||||
mipped_prepared.runtime = &mipped_runtime;
|
||||
mipped_prepared.images.push_back(
|
||||
std::move(plain_mipped_storage_binding));
|
||||
mipped_prepared.images.push_back(
|
||||
@@ -9516,9 +9578,8 @@ public:
|
||||
split_program.info = std::move(split_ir.info);
|
||||
split_program.bindings = std::move(split_ir.bindings);
|
||||
PreparedBindings split_bindings{};
|
||||
ShaderRecompiler::IR::ResourceSnapshot split_snapshot{};
|
||||
split_bindings.program = &split_program;
|
||||
split_bindings.snapshot = &split_snapshot;
|
||||
ShaderStageRuntime split_runtime{.program = &split_program};
|
||||
split_bindings.runtime = &split_runtime;
|
||||
split_bindings.images.push_back(
|
||||
{split_id, texture_cache.FindTexture(split_id, split_storage_desc),
|
||||
split_storage_desc});
|
||||
@@ -9594,7 +9655,7 @@ public:
|
||||
registers.SetRenderControl({});
|
||||
RenderDepthInfo disabled_depth{};
|
||||
RenderExecutorTestAccess::ResolveRenderDepthTarget(
|
||||
executor, 1, scheduler.Current(), disabled_depth);
|
||||
executor, scheduler.Current(), disabled_depth);
|
||||
Require(name, "disabled tests with stale depth write enable",
|
||||
!disabled_depth.image_id,
|
||||
"a dormant depth write bit bound a depth attachment");
|
||||
@@ -9606,7 +9667,7 @@ public:
|
||||
|
||||
RenderDepthInfo phased_depth{};
|
||||
RenderExecutorTestAccess::ResolveRenderDepthTarget(
|
||||
executor, 1, scheduler.Current(), phased_depth);
|
||||
executor, scheduler.Current(), phased_depth);
|
||||
auto non_texture_compatible_target = phased_depth_target;
|
||||
non_texture_compatible_target.z_info.texture_compatibility =
|
||||
Prospero::TextureCompatiblePlaneCompression::kDisable;
|
||||
@@ -9615,7 +9676,7 @@ public:
|
||||
registers.SetDepthRenderTarget(non_texture_compatible_target);
|
||||
RenderDepthInfo non_texture_compatible_depth{};
|
||||
RenderExecutorTestAccess::ResolveRenderDepthTarget(
|
||||
executor, 1, scheduler.Current(), non_texture_compatible_depth);
|
||||
executor, scheduler.Current(), non_texture_compatible_depth);
|
||||
Require(
|
||||
name, "depth texture compatibility identity",
|
||||
phased_depth.image_id && (phased_depth.desc.info.metadata.kind == ImageMetadataKind::Htile) &&
|
||||
@@ -9628,7 +9689,6 @@ public:
|
||||
phased_depth.desc.info.metadata.range &&
|
||||
phased_depth.desc.info.metadata.stencil_compressed &&
|
||||
!phased_depth.depth_clear_enable &&
|
||||
!phased_depth.depth_meta_clear_enable &&
|
||||
!texture_cache.IsMeta(phased_depth.desc.info.metadata.range.address) &&
|
||||
!texture_cache.GetImage(phased_depth.image_id).IsGpuModified() &&
|
||||
!texture_cache.GetImage(phased_depth.image_id).usage.depth_target,
|
||||
@@ -9661,7 +9721,7 @@ public:
|
||||
registers.SetStencilMask(read_only_stencil_mask);
|
||||
RenderDepthInfo read_only_depth{};
|
||||
RenderExecutorTestAccess::ResolveRenderDepthTarget(
|
||||
executor, 1, scheduler.Current(), read_only_depth);
|
||||
executor, scheduler.Current(), read_only_depth);
|
||||
Require(
|
||||
name, "read-only depth/stencil target without write addresses",
|
||||
read_only_depth.image_id == phased_depth.image_id &&
|
||||
@@ -9685,7 +9745,7 @@ public:
|
||||
registers.SetRenderControl(read_only_render_control);
|
||||
RenderDepthInfo read_only_clear_depth{};
|
||||
RenderExecutorTestAccess::ResolveRenderDepthTarget(
|
||||
executor, 1, scheduler.Current(), read_only_clear_depth);
|
||||
executor, scheduler.Current(), read_only_clear_depth);
|
||||
Require(name, "read-only stencil clear suppression",
|
||||
read_only_clear_depth.image_id == phased_depth.image_id &&
|
||||
!read_only_clear_depth.stencil_clear_enable &&
|
||||
@@ -9707,7 +9767,7 @@ public:
|
||||
phased_rendering.depth_stencil_attachment.image_view != nullptr &&
|
||||
phased_rendering.num_color_attachments == 0 &&
|
||||
phased_rendering.depth_stencil_attachment.has_depth &&
|
||||
phased_depth.depth_meta_clear_enable &&
|
||||
phased_rendering.depth_stencil_attachment.depth_clear &&
|
||||
phased_depth.depth_load_clear_enable &&
|
||||
texture_cache.IsMeta(phased_depth.desc.info.metadata.range.address) &&
|
||||
!texture_cache.IsMetaCleared(
|
||||
@@ -9745,7 +9805,7 @@ public:
|
||||
registers.SetRenderControl(depth_only_render_control);
|
||||
RenderDepthInfo depth_only{};
|
||||
RenderExecutorTestAccess::ResolveRenderDepthTarget(
|
||||
executor, 1, scheduler.Current(), depth_only);
|
||||
executor, scheduler.Current(), depth_only);
|
||||
Require(
|
||||
name, "depth-only target with stale stencil state",
|
||||
depth_only.image_id && depth_only.desc.view_info.format == vk::Format::eD32Sfloat &&
|
||||
@@ -9870,7 +9930,7 @@ public:
|
||||
}
|
||||
RenderDepthInfo bounds_depth{};
|
||||
RenderExecutorTestAccess::ResolveRenderDepthTarget(
|
||||
executor, 1, scheduler.Current(), bounds_depth);
|
||||
executor, scheduler.Current(), bounds_depth);
|
||||
auto bounds_bindings = RenderExecutorTestAccess::PrepareGraphicsBindings(
|
||||
executor, bounds_vertex, bounds_pixel, true);
|
||||
const auto bounds_rendering = RenderExecutorTestAccess::AcquireRenderTargets(
|
||||
@@ -9884,7 +9944,6 @@ public:
|
||||
bounds_depth.image_id == depth_only.image_id &&
|
||||
bounds_depth.depth_bounds_test_enable &&
|
||||
!bounds_depth.depth_write_enable &&
|
||||
bounds_depth.depth_meta_clear_enable == (pass == 0) &&
|
||||
bounds_depth.depth_load_clear_enable == (pass == 0) &&
|
||||
!texture_cache.IsMetaCleared(depth_only_htile_address, 0) &&
|
||||
bounds_rendering.depth_stencil_attachment.image_layout == readonly_layout &&
|
||||
@@ -9955,7 +10014,7 @@ public:
|
||||
registers.SetStencilMask(shared_stencil_mask);
|
||||
RenderDepthInfo shared_depth{};
|
||||
RenderExecutorTestAccess::ResolveRenderDepthTarget(
|
||||
executor, 1, scheduler.Current(), shared_depth);
|
||||
executor, scheduler.Current(), shared_depth);
|
||||
auto shared_bindings =
|
||||
RenderExecutorTestAccess::PrepareGraphicsBindings(
|
||||
executor, shared_depth_vertex, shared_depth_pixel, true);
|
||||
@@ -11475,7 +11534,8 @@ public:
|
||||
test.fragment_code.push_back(EncodeExp0(0x08, 0x1, false));
|
||||
test.fragment_code.push_back(EncodeExp1(1, 1, 1, 1));
|
||||
} else {
|
||||
AppendVMovLiteral(&test.fragment_code, 0, 0x3f800000u);
|
||||
test.pixel_interpolator_settings = {0x400u};
|
||||
test.fragment_code.push_back(EncodeVintrp(0x02, 0, 0, 0, 2));
|
||||
}
|
||||
test.fragment_code.push_back(EncodeExp0(0x00, 0xf));
|
||||
test.fragment_code.push_back(EncodeExp1(0, 0, 0, 0));
|
||||
@@ -11524,8 +11584,8 @@ public:
|
||||
color.desc.view_info.usage = vk::ImageUsageFlagBits::eColorAttachment;
|
||||
color.image_id = cache.FindImage(color.desc);
|
||||
std::array<float, 18> vertices{
|
||||
-0.75f, -0.75f, 1, 1, 1, 1, 0.75f, -0.75f, 1, 1, 1, 1,
|
||||
0.0f, 0.75f, 1, 1, 1, 1};
|
||||
-0.75f, -0.75f, 1, 1, 1, 1, 0.75f, -0.75f, 0.5f, 1, 1, 1,
|
||||
0.0f, 0.75f, 0.25f, 1, 1, 1};
|
||||
if (depth_feedback) {
|
||||
vertices = {-1, -1, 0, 0, 0, 1, 3, -1, 2, 0, 0, 1, -1, 3, 0, 2, 0, 1};
|
||||
}
|
||||
@@ -11533,11 +11593,13 @@ public:
|
||||
std::memcpy(vertex_words.data(), vertices.data(), sizeof(vertices));
|
||||
auto buffer = CreateHostBuffer(name, sizeof(vertices), vk::BufferUsageFlagBits::eVertexBuffer,
|
||||
vertex_words);
|
||||
const auto pipeline = [&](bool enabled, uint8_t front, uint8_t back) -> PipelineCache::Pipeline & {
|
||||
const auto pipeline = [&](bool enabled, uint8_t front, uint8_t back,
|
||||
bool provoking_last = false) -> PipelineCache::Pipeline & {
|
||||
HW::ModeControl mode{};
|
||||
mode.poly_mode = enabled;
|
||||
mode.polymode_front_ptype = front;
|
||||
mode.polymode_back_ptype = back;
|
||||
mode.provoking_vtx_last = provoking_last;
|
||||
registers.SetModeControl(mode);
|
||||
return context.GetPipelineCache().CreateGraphicsPipeline(
|
||||
std::span{&color, 1u}, depth, vertex, scheduler.Current(), &pixel,
|
||||
@@ -11627,6 +11689,19 @@ public:
|
||||
line_pixels[interior] == 0 &&
|
||||
std::ranges::any_of(line_pixels, [](u32 value) { return value == 0x3f800000u; }),
|
||||
"wireframe did not preserve triangle edges while leaving its interior empty");
|
||||
auto &last_vertex = pipeline(true, 2, 2, true);
|
||||
Require(name, "provoking vertex pipeline cache",
|
||||
last_vertex.pipeline != filled.pipeline &&
|
||||
pipeline(true, 2, 2, true).pipeline == last_vertex.pipeline &&
|
||||
pipeline(true, 2, 2).pipeline == filled.pipeline,
|
||||
"first and last provoking vertices did not keep distinct cached pipelines");
|
||||
draw(last_vertex);
|
||||
const auto last_pixels = read_color();
|
||||
for (size_t i = 0; i < solid_pixels.size(); i++) {
|
||||
Require(name, "flat provoking vertex output",
|
||||
last_pixels[i] == (solid_pixels[i] == 0 ? 0 : 0x3e800000u),
|
||||
"last-vertex flat shading changed coverage or did not use vertex two");
|
||||
}
|
||||
}
|
||||
resources.UnmapMemory(depth_address, allocation_size);
|
||||
scheduler.Finish();
|
||||
@@ -12938,6 +13013,7 @@ private:
|
||||
m_runtime_context.queue_family = m_queue_family;
|
||||
m_runtime_context.queue = m_queue;
|
||||
m_runtime_context.attachment_feedback_loop_enabled = true;
|
||||
m_runtime_context.provoking_vertex_last_enabled = true;
|
||||
|
||||
VmaVulkanFunctions functions{};
|
||||
functions.vkGetInstanceProcAddr =
|
||||
@@ -13050,9 +13126,11 @@ private:
|
||||
available_feedback_layout.pNext = &available_color_write;
|
||||
vk::PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT available_feedback_dynamic{};
|
||||
available_feedback_dynamic.pNext = &available_feedback_layout;
|
||||
vk::PhysicalDeviceProvokingVertexFeaturesEXT available_provoking_vertex{};
|
||||
available_provoking_vertex.pNext = &available_feedback_dynamic;
|
||||
vk::PhysicalDeviceFeatures2 available_features2{};
|
||||
available_features2.sType = vk::StructureType::ePhysicalDeviceFeatures2;
|
||||
available_features2.pNext = &available_feedback_dynamic;
|
||||
available_features2.pNext = &available_provoking_vertex;
|
||||
m_physical_device.getFeatures2(&available_features2);
|
||||
Require("VulkanHarness", "dispatch",
|
||||
available_features.shaderStorageImageWriteWithoutFormat == true,
|
||||
@@ -13084,7 +13162,8 @@ private:
|
||||
available_depth_clip.depthClipEnable && available_clip_control.depthClipControl &&
|
||||
available_color_write.colorWriteEnable &&
|
||||
available_feedback_layout.attachmentFeedbackLoopLayout &&
|
||||
available_feedback_dynamic.attachmentFeedbackLoopDynamicState,
|
||||
available_feedback_dynamic.attachmentFeedbackLoopDynamicState &&
|
||||
available_provoking_vertex.provokingVertexLast,
|
||||
"production rasterization features are not supported");
|
||||
|
||||
float priority = 1.0f;
|
||||
@@ -13132,7 +13211,10 @@ private:
|
||||
vk::PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT feedback_dynamic{};
|
||||
feedback_dynamic.pNext = &feedback_layout;
|
||||
feedback_dynamic.attachmentFeedbackLoopDynamicState = true;
|
||||
device_info.pNext = &feedback_dynamic;
|
||||
vk::PhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex{};
|
||||
provoking_vertex.pNext = &feedback_dynamic;
|
||||
provoking_vertex.provokingVertexLast = available_provoking_vertex.provokingVertexLast;
|
||||
device_info.pNext = &provoking_vertex;
|
||||
vk::PhysicalDeviceFeatures device_features{};
|
||||
device_features.shaderStorageImageWriteWithoutFormat = true;
|
||||
device_features.shaderImageGatherExtended = true;
|
||||
@@ -13148,7 +13230,8 @@ private:
|
||||
VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME,
|
||||
VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME,
|
||||
VK_EXT_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_EXTENSION_NAME,
|
||||
VK_EXT_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_EXTENSION_NAME};
|
||||
VK_EXT_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_EXTENSION_NAME,
|
||||
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME};
|
||||
device_info.enabledExtensionCount = std::size(device_extensions);
|
||||
device_info.ppEnabledExtensionNames = device_extensions;
|
||||
RequireVk("VulkanHarness", "dispatch",
|
||||
@@ -28124,6 +28207,11 @@ int main(int argc, char **argv) {
|
||||
vulkan.CheckSchedulerTimeline();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--host-image-allocation-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckHostImageAllocation();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--occlusion-dump-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
CheckPm4SyntheticOcclusionCounterDump(vulkan.RuntimeRenderer());
|
||||
@@ -28436,6 +28524,7 @@ int main(int argc, char **argv) {
|
||||
CheckIndirectImageKeySwitch();
|
||||
CheckPs5GameExampleImageClearRuntimeShape();
|
||||
vulkan.CheckSchedulerTimeline();
|
||||
vulkan.CheckHostImageAllocation();
|
||||
vulkan.CheckDescriptorHeapLargeSet();
|
||||
vulkan.CheckGraphicsPushConstantBank();
|
||||
vulkan.CheckGpuMappedRangeLifecycle();
|
||||
|
||||
+52
-11
@@ -4328,7 +4328,7 @@ void TestNewShaderRecompilerCapturedVopcSdwaCmpxClass() {
|
||||
Check(decoded.family == Decoder::Family::VOPC &&
|
||||
decoded.opcode == Decoder::Opcode::V_CMPX_CLASS_F32 &&
|
||||
decoded.opcode_id == 0x98u && decoded.word_count == 2u &&
|
||||
decoded.raw_count == 2u &&
|
||||
decoded.raw[0] == shader[0] && decoded.raw[1] == shader[1] &&
|
||||
decoded.dst.kind == Decoder::OperandKind::ExecLo &&
|
||||
decoded.src_count == 2u &&
|
||||
decoded.src0.kind == Decoder::OperandKind::Vgpr &&
|
||||
@@ -9117,6 +9117,8 @@ void TestMeshExportStorage() {
|
||||
EncodeExp0(0x14, 0x1), EncodeExp1(0, 0, 0, 0), // primitive
|
||||
EncodeSopp(0x01),
|
||||
};
|
||||
std::vector<uint32_t> monolithic(std::begin(front), std::begin(front) + 2);
|
||||
monolithic.insert(monolithic.end(), std::begin(back), std::end(back));
|
||||
ShaderVertexInputInfo input{};
|
||||
input.pa_cl_vs_out_cntl = (1u << 21u) | (1u << 18u);
|
||||
auto &mesh = input.mesh;
|
||||
@@ -9136,16 +9138,23 @@ void TestMeshExportStorage() {
|
||||
ShaderRecompiler::CompileOptions options{};
|
||||
options.stage = ShaderType::Mesh;
|
||||
options.input_info.vertex = &input;
|
||||
options.back_code = back;
|
||||
options.user_data = user_data;
|
||||
for (const auto [subgroup_size, push_data_start] :
|
||||
{std::pair{32u, PushData::MeshDrawDwordCount},
|
||||
std::pair{64u, PushData::MeshDrawDwordCount},
|
||||
std::pair{32u, PushData::DwordCount},
|
||||
std::pair{64u, PushData::DwordCount}}) {
|
||||
struct Case {
|
||||
uint32_t subgroup_size, push_data_start;
|
||||
bool split;
|
||||
};
|
||||
const Case cases[] = {
|
||||
{32u, PushData::MeshDrawDwordCount, true}, {64u, PushData::MeshDrawDwordCount, true},
|
||||
{32u, PushData::DwordCount, true}, {64u, PushData::DwordCount, true},
|
||||
{32u, PushData::MeshDrawDwordCount, false}, {64u, PushData::MeshDrawDwordCount, false},
|
||||
{32u, PushData::DwordCount, false}, {64u, PushData::DwordCount, false},
|
||||
};
|
||||
for (const auto [subgroup_size, push_data_start, split] : cases) {
|
||||
mesh.host_subgroup_size = subgroup_size;
|
||||
const auto result =
|
||||
RecompileForTest(front, options, nullptr, nullptr, push_data_start);
|
||||
options.back_code = split ? std::span{back} : std::span<const uint32_t>{};
|
||||
const auto result = RecompileForTest(
|
||||
split ? std::span{front} : std::span<const uint32_t>{monolithic},
|
||||
options, nullptr, nullptr, push_data_start);
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
const auto &layout = result.program.bindings;
|
||||
Check(layout.UsesPushData() == (push_data_start == PushData::MeshDrawDwordCount) &&
|
||||
@@ -9232,9 +9241,13 @@ void TestMergedShaderUserDataSnapshot() {
|
||||
const std::array<uint32_t, 4> front_data = {regs.gs_user_sgpr.value[0],
|
||||
regs.gs_user_sgpr.value[1], regs.gs_user_sgpr.value[2], regs.gs_user_sgpr.value[3]};
|
||||
ShaderMappedData mapped{};
|
||||
mapped.type = Prospero::ShaderBinaryType::kGsFront;
|
||||
mapped.code_size_bytes = sizeof(front);
|
||||
mapped.scratch_size_dwords = 3;
|
||||
ShaderMapUserData(regs.es_regs.data_addr, mapped);
|
||||
mapped.type = Prospero::ShaderBinaryType::kGsBack;
|
||||
mapped.code_size_bytes = sizeof(back);
|
||||
mapped.scratch_size_dwords = 7;
|
||||
ShaderMapUserData(regs.gs_regs.data_addr, mapped);
|
||||
HW::Context context;
|
||||
context.SetShaderStages(0x20);
|
||||
@@ -9255,8 +9268,9 @@ void TestMergedShaderUserDataSnapshot() {
|
||||
second.user_data[8] == front_data[0] + 1,
|
||||
"merged shader parameters did not snapshot ordinary user SGPRs at s8");
|
||||
Check(first.hash == second.hash &&
|
||||
MakeStageStaticKey(first_input) == MakeStageStaticKey(second_input),
|
||||
"a dynamic merged-shader user-data pointer changed shader identity");
|
||||
MakeStageStaticKey(first_input) == MakeStageStaticKey(second_input) &&
|
||||
first_input.mesh.scratch_size_dwords == 7,
|
||||
"merged shader identity or maximum scratch size changed with dynamic user data");
|
||||
CompileOptions options{};
|
||||
options.stage = ShaderType::Mesh;
|
||||
options.user_data_base = 0;
|
||||
@@ -9281,6 +9295,25 @@ void TestMergedShaderUserDataSnapshot() {
|
||||
std::equal(table.begin(), table.end(), back_descriptor.dwords.begin()),
|
||||
"merged shader resource plan did not follow the current s0:s1 pointer and s8 data");
|
||||
}
|
||||
const uint32_t monolithic[] = {
|
||||
EncodeMubuf0(0x1c), EncodeMubuf1(0, 2, 1), EncodeSopp(0x01),
|
||||
};
|
||||
regs.es_regs.data_addr = reinterpret_cast<uint64_t>(monolithic);
|
||||
mapped.type = Prospero::ShaderBinaryType::kGs;
|
||||
mapped.code_size_bytes = sizeof(monolithic);
|
||||
mapped.scratch_size_dwords = 3;
|
||||
ShaderMapUserData(regs.es_regs.data_addr, mapped);
|
||||
for (const auto back_address : {regs.gs_regs.data_addr, uint64_t{0}}) {
|
||||
regs.gs_regs.data_addr = back_address;
|
||||
ShaderVertexInputInfo input{};
|
||||
const auto params = PrepareProgram(regs, context, user_config, input);
|
||||
Check(params.back_code.empty() && params.hash == XXH3_64bits(monolithic, sizeof(monolithic)) &&
|
||||
input.mesh.scratch_size_dwords == 3 && params.user_data.size() == 12 &&
|
||||
params.user_data[0] == 0 && params.user_data[1] == 0 &&
|
||||
std::equal(second.user_data.begin() + 8, second.user_data.end(),
|
||||
params.user_data.begin() + 8),
|
||||
"monolithic NGG shader used stale GS-back state or lost its s8 user data");
|
||||
}
|
||||
}
|
||||
|
||||
void TestEmbeddedVertexFormatSwizzle() {
|
||||
@@ -9418,6 +9451,14 @@ void TestMeshInputAssembly() {
|
||||
0x40000305, 1, 2, 3, 0, 15, false},
|
||||
{Prospero::PrimitiveType::kTriStrip, 5, 8, 0, 1, 0, 0, 11,
|
||||
0x40000305, 2, 1, 3, 0, 12, false},
|
||||
{Prospero::PrimitiveType::kLineList, 5, 17, 3, 0, 0, 0, 11,
|
||||
0x40000204, 0, 1, 0, 0, 23, false},
|
||||
{Prospero::PrimitiveType::kLineList, 6, 17, 2, 4, 2, 0x1002, 11,
|
||||
0x40000205, 8, 9, 0, 32, 0xabd8, true},
|
||||
{Prospero::PrimitiveType::kLineList, 6, 17, 2, 5, 2, 0x1002, 0,
|
||||
0x40000205, 10, 11, 0, 36, 0, false},
|
||||
{Prospero::PrimitiveType::kLineList, 8, 180, 1, 64, 4, 0x1000, 0,
|
||||
0x41000000, 128, 129, 0, 288, 0, false},
|
||||
{Prospero::PrimitiveType::kPointList, 12, 265, 22, 0, 2, 0x1002, 0,
|
||||
0x40000101, 0, 0, 0, 528, 0xabcd, true},
|
||||
{Prospero::PrimitiveType::kPointList, 12, 265, 22, 1, 2, 0x1002, 0,
|
||||
|
||||
Reference in new issue
Block a user