mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4ad5fc988 | ||
|
|
c0d3d261ea | ||
|
|
3b75a5659a |
@@ -27,8 +27,9 @@ Development is focused on compatibility and boot reliability.
|
||||
Windows is the primary platform and receives the most testing. Linux builds and runs; see
|
||||
[Building on Linux](#building-on-linux).
|
||||
|
||||
macOS support is experimental. Compatibility with the same games on Windows and macOS has not yet
|
||||
been tested.
|
||||
macOS support is experimental. The emulator is built for x86-64 and runs on Apple Silicon under
|
||||
Rosetta 2, with Vulkan provided by MoltenVK. A small number of titles have been verified in-game
|
||||
on Apple Silicon hardware; see [Building on macOS](#building-on-macos).
|
||||
|
||||
## Bugs and Issues
|
||||
|
||||
@@ -114,9 +115,10 @@ the Vulkan/SPIR-V validation rules.
|
||||
|
||||
### System requirements
|
||||
|
||||
- Windows 10 version 1803, or a current Linux distribution
|
||||
- A 64-bit x86 processor
|
||||
- A Vulkan 1.3-capable GPU with current drivers
|
||||
- Windows 10 version 1803, a current Linux distribution, or macOS on Apple Silicon
|
||||
- A 64-bit x86 processor (on macOS, an Apple Silicon processor with Rosetta 2)
|
||||
- A Vulkan 1.3-capable GPU with current drivers (on macOS, Vulkan is provided by the bundled
|
||||
MoltenVK)
|
||||
|
||||
### Build requirements (Windows)
|
||||
|
||||
@@ -188,6 +190,45 @@ time.
|
||||
|
||||
Note that the CMake source root is `src`, not the repository root.
|
||||
|
||||
### Building on macOS
|
||||
|
||||
macOS builds target x86-64 and run under Rosetta 2 on Apple Silicon, so the PS5's x86-64 game
|
||||
code executes through the same translation layer as the emulator itself. Prebuilt archives are
|
||||
attached to releases; the steps below are for building from source.
|
||||
|
||||
Requirements:
|
||||
|
||||
- An Apple Silicon Mac with Rosetta 2 installed (`softwareupdate --install-rosetta`)
|
||||
- Xcode (or the Command Line Tools)
|
||||
- Homebrew packages: `brew install cmake ninja glslang`
|
||||
- Qt 6 (Concurrent, Network, Widgets) with x86-64 support. The official Qt installation is
|
||||
universal and works; Homebrew's Qt is arm64-only and will not link
|
||||
|
||||
```bash
|
||||
git submodule update --init --recursive
|
||||
|
||||
cmake -S src -B _Build/macos -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
||||
-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DCMAKE_PREFIX_PATH="$Qt6_DIR"
|
||||
|
||||
cmake --build _Build/macos --target launcher --parallel
|
||||
cmake --install _Build/macos --prefix _Build/macos/install
|
||||
```
|
||||
|
||||
The build re-signs `kyty_emulator` with the JIT entitlements it needs to execute translated
|
||||
guest code; no manual signing step is required.
|
||||
|
||||
Vulkan comes from MoltenVK. Download `MoltenVK-macos.tar` from the
|
||||
[MoltenVK releases](https://github.com/KhronosGroup/MoltenVK/releases), then copy
|
||||
`MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib` next to `kyty_emulator` and ad-hoc sign it:
|
||||
|
||||
```bash
|
||||
codesign --force --sign - _Build/macos/install/libMoltenVK.dylib
|
||||
```
|
||||
|
||||
Release archives already include a signed `libMoltenVK.dylib`.
|
||||
|
||||
### Visual Studio Code
|
||||
|
||||
A ready-made Visual Studio Code setup is included in [`.vscode`](.vscode). It configures CMake
|
||||
@@ -233,6 +274,14 @@ The emulator can also be started directly with a legally obtained game directory
|
||||
./_Build/linux/install/kyty_emulator --game "/games/ExampleGame"
|
||||
```
|
||||
|
||||
On macOS, point SDL at the MoltenVK library explicitly; the hardened runtime prevents it from
|
||||
being picked up from the executable's directory:
|
||||
|
||||
```bash
|
||||
cd _Build/macos/install
|
||||
SDL_VULKAN_LIBRARY="$PWD/libMoltenVK.dylib" ./kyty_emulator --game "/games/ExampleGame"
|
||||
```
|
||||
|
||||
Run `kyty_emulator --help` to see the available graphics, logging, validation, profiling, and
|
||||
debugging options.
|
||||
|
||||
|
||||
@@ -2,6 +2,66 @@
|
||||
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
namespace {
|
||||
|
||||
uint32_t EmitCubeAxisF32(EmitterState& state, uint32_t value) {
|
||||
const auto normalized = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpFSub, state.float_type, normalized, value, ConstantF32(state, 0x3f800000u)});
|
||||
return normalized;
|
||||
}
|
||||
|
||||
uint32_t EmitCubeLayerF32(EmitterState& state, uint32_t face_id) {
|
||||
// Sampled RDNA2 cubemaps encode face_id as slice * 8 + face. The native
|
||||
// 2D-array view stores six contiguous faces per slice, so remove the two
|
||||
// reserved face IDs from every preceding slice.
|
||||
const auto guest_layer = state.builder.AllocateId();
|
||||
const auto slice = state.builder.AllocateId();
|
||||
const auto padding = state.builder.AllocateId();
|
||||
const auto host_layer = state.builder.AllocateId();
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpConvertFToU, state.uint_type, guest_layer, face_id});
|
||||
state.builder.AddFunction(
|
||||
{OpShiftRightLogical, state.uint_type, slice, guest_layer, ConstantU32(state, 3)});
|
||||
state.builder.AddFunction(
|
||||
{OpShiftLeftLogical, state.uint_type, padding, slice, ConstantU32(state, 1)});
|
||||
state.builder.AddFunction({OpISub, state.uint_type, host_layer, guest_layer, padding});
|
||||
state.builder.AddFunction({OpConvertUToF, state.float_type, result, host_layer});
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t EmitImageCoordF32Impl(EmitterState& state, const IR::Instruction& inst,
|
||||
const IR::Operand& address, uint32_t first_component,
|
||||
uint32_t components) {
|
||||
auto x = EmitImageAddressFloatLoad(state, inst, address, first_component);
|
||||
if (components == 1u) {
|
||||
return x;
|
||||
}
|
||||
auto y = inst.memory.image_address_components > first_component + 1u
|
||||
? EmitImageAddressFloatLoad(state, inst, address, first_component + 1u)
|
||||
: EmitZeroF32(state);
|
||||
if (inst.memory.image_cube) {
|
||||
// RDNA2 sampled cubemap S/T coordinates are biased by +1 relative to
|
||||
// normalized 2D-array coordinates.
|
||||
x = EmitCubeAxisF32(state, x);
|
||||
y = EmitCubeAxisF32(state, y);
|
||||
}
|
||||
const auto coord = state.builder.AllocateId();
|
||||
if (components == 3u) {
|
||||
auto z = inst.memory.image_address_components > first_component + 2u
|
||||
? EmitImageAddressFloatLoad(state, inst, address, first_component + 2u)
|
||||
: EmitZeroF32(state);
|
||||
if (inst.memory.image_cube) {
|
||||
z = EmitCubeLayerF32(state, z);
|
||||
}
|
||||
state.builder.AddFunction({OpCompositeConstruct, state.vec3_float_type, coord, x, y, z});
|
||||
} else {
|
||||
state.builder.AddFunction({OpCompositeConstruct, state.vec2_float_type, coord, x, y});
|
||||
}
|
||||
return coord;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool HasImageSampleFlag(const IR::Instruction& inst, uint32_t flag) {
|
||||
return (inst.memory.image_sample_flags & flag) != 0;
|
||||
@@ -21,7 +81,7 @@ ImageSampleLayout MakeImageSampleLayout(const IR::Instruction& inst, ImageViewKi
|
||||
}
|
||||
if (HasImageSampleFlag(inst, Decoder::ImageSampleFlagDerivative)) {
|
||||
const auto components = ImageViewSpatialComponents(view);
|
||||
layout.grad_x = cursor;
|
||||
layout.grad_x = cursor;
|
||||
cursor += components;
|
||||
layout.grad_y = cursor;
|
||||
cursor += components;
|
||||
@@ -36,24 +96,8 @@ ImageSampleLayout MakeImageSampleLayout(const IR::Instruction& inst, ImageViewKi
|
||||
|
||||
uint32_t EmitImageCoordF32(EmitterState& state, const IR::Instruction& inst,
|
||||
const ImageSampleLayout& layout, ImageViewKind view) {
|
||||
const auto x = EmitImageAddressFloatLoad(state, inst, inst.src[0], layout.coord);
|
||||
const auto components = ImageViewCoordinateComponents(view);
|
||||
if (components == 1u) {
|
||||
return x;
|
||||
}
|
||||
const auto y = inst.memory.image_address_components > layout.coord + 1u
|
||||
? EmitImageAddressFloatLoad(state, inst, inst.src[0], layout.coord + 1u)
|
||||
: EmitZeroF32(state);
|
||||
const auto coord = state.builder.AllocateId();
|
||||
if (components == 3u) {
|
||||
const auto z = inst.memory.image_address_components > layout.coord + 2u
|
||||
? EmitImageAddressFloatLoad(state, inst, inst.src[0], layout.coord + 2u)
|
||||
: EmitZeroF32(state);
|
||||
state.builder.AddFunction({OpCompositeConstruct, state.vec3_float_type, coord, x, y, z});
|
||||
} else {
|
||||
state.builder.AddFunction({OpCompositeConstruct, state.vec2_float_type, coord, x, y});
|
||||
}
|
||||
return coord;
|
||||
return EmitImageCoordF32Impl(state, inst, inst.src[0], layout.coord,
|
||||
ImageViewCoordinateComponents(view));
|
||||
}
|
||||
|
||||
uint32_t EmitImageLodF32(EmitterState& state, const IR::Instruction& inst,
|
||||
@@ -95,10 +139,10 @@ uint32_t EmitImageGradientF32(EmitterState& state, const IR::Instruction& inst,
|
||||
: EmitZeroF32(state);
|
||||
const auto grad = state.builder.AllocateId();
|
||||
if (components == 3u) {
|
||||
const auto z = inst.memory.image_address_components > first_component + 2u
|
||||
? EmitImageAddressFloatLoad(state, inst, inst.src[0],
|
||||
first_component + 2u)
|
||||
: EmitZeroF32(state);
|
||||
const auto z =
|
||||
inst.memory.image_address_components > first_component + 2u
|
||||
? EmitImageAddressFloatLoad(state, inst, inst.src[0], first_component + 2u)
|
||||
: EmitZeroF32(state);
|
||||
state.builder.AddFunction({OpCompositeConstruct, state.vec3_float_type, grad, x, y, z});
|
||||
} else {
|
||||
state.builder.AddFunction({OpCompositeConstruct, state.vec2_float_type, grad, x, y});
|
||||
@@ -120,8 +164,7 @@ uint32_t EmitImagePackedOffsetI32(EmitterState& state, const IR::Instruction& in
|
||||
state.builder.AddFunction(
|
||||
{OpCompositeConstruct, state.vec3_int_type, ret, zero, zero, zero});
|
||||
} else {
|
||||
state.builder.AddFunction(
|
||||
{OpCompositeConstruct, state.vec2_int_type, ret, zero, zero});
|
||||
state.builder.AddFunction({OpCompositeConstruct, state.vec2_int_type, ret, zero, zero});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -131,18 +174,18 @@ uint32_t EmitImagePackedOffsetI32(EmitterState& state, const IR::Instruction& in
|
||||
const auto offset_x = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitcast, state.int_type, packed_i32, packed_bits});
|
||||
state.builder.AddFunction({OpBitFieldSExtract, state.int_type, offset_x, packed_i32,
|
||||
ConstantI32(state, 0), ConstantI32(state, 6)});
|
||||
ConstantI32(state, 0), ConstantI32(state, 6)});
|
||||
if (components == 1u) {
|
||||
return offset_x;
|
||||
}
|
||||
const auto offset_y = state.builder.AllocateId();
|
||||
const auto offset = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitFieldSExtract, state.int_type, offset_y, packed_i32,
|
||||
ConstantI32(state, 8), ConstantI32(state, 6)});
|
||||
ConstantI32(state, 8), ConstantI32(state, 6)});
|
||||
if (components == 3u) {
|
||||
const auto offset_z = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitFieldSExtract, state.int_type, offset_z, packed_i32,
|
||||
ConstantI32(state, 16), ConstantI32(state, 6)});
|
||||
ConstantI32(state, 16), ConstantI32(state, 6)});
|
||||
state.builder.AddFunction(
|
||||
{OpCompositeConstruct, state.vec3_int_type, offset, offset_x, offset_y, offset_z});
|
||||
} else {
|
||||
@@ -158,7 +201,7 @@ uint32_t EmitImageCoordU32(EmitterState& state, const IR::Instruction& inst, Ima
|
||||
if (components == 1u) {
|
||||
return x;
|
||||
}
|
||||
const auto y = inst.memory.image_address_components > 1u
|
||||
const auto y = inst.memory.image_address_components > 1u
|
||||
? EmitImageAddressValueLoad(state, inst, inst.src[1], 1)
|
||||
: ConstantU32(state, 0);
|
||||
const auto coord = state.builder.AllocateId();
|
||||
@@ -180,7 +223,7 @@ uint32_t EmitImageLoadCoordU32(EmitterState& state, const IR::Instruction& inst,
|
||||
if (components == 1u) {
|
||||
return x;
|
||||
}
|
||||
const auto y = inst.memory.image_address_components > 1u
|
||||
const auto y = inst.memory.image_address_components > 1u
|
||||
? EmitImageAddressValueLoad(state, inst, inst.src[0], 1)
|
||||
: ConstantU32(state, 0);
|
||||
const auto coord = state.builder.AllocateId();
|
||||
@@ -209,24 +252,8 @@ uint32_t EmitImageMipLodU32(EmitterState& state, const IR::Instruction& inst,
|
||||
|
||||
uint32_t EmitImageQueryCoordF32(EmitterState& state, const IR::Instruction& inst,
|
||||
ImageViewKind view) {
|
||||
const auto x = EmitImageAddressFloatLoad(state, inst, inst.src[0], 0);
|
||||
const auto components = ImageViewCoordinateComponents(view);
|
||||
if (components == 1u) {
|
||||
return x;
|
||||
}
|
||||
const auto y = inst.memory.image_address_components > 1u
|
||||
? EmitImageAddressFloatLoad(state, inst, inst.src[0], 1)
|
||||
: EmitZeroF32(state);
|
||||
const auto coord = state.builder.AllocateId();
|
||||
if (components == 3u) {
|
||||
const auto z = inst.memory.image_address_components > 2u
|
||||
? EmitImageAddressFloatLoad(state, inst, inst.src[0], 2)
|
||||
: EmitZeroF32(state);
|
||||
state.builder.AddFunction({OpCompositeConstruct, state.vec3_float_type, coord, x, y, z});
|
||||
} else {
|
||||
state.builder.AddFunction({OpCompositeConstruct, state.vec2_float_type, coord, x, y});
|
||||
}
|
||||
return coord;
|
||||
// OpImageQueryLod takes only the spatial coordinates, even for arrayed images.
|
||||
return EmitImageCoordF32Impl(state, inst, inst.src[0], 0, ImageViewSpatialComponents(view));
|
||||
}
|
||||
|
||||
uint32_t DmaskComponentIndex(uint32_t dmask, uint32_t component) {
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace {
|
||||
|
||||
constexpr uint64_t AddressMask = 0x0000ffffffffffffull;
|
||||
|
||||
Decoder::ImageDimension DescriptorDimension(const DescriptorValue& descriptor,
|
||||
Decoder::ImageDimension DescriptorDimension(const DescriptorValue& descriptor,
|
||||
Decoder::ImageDimension requested) {
|
||||
const bool is_array = requested == Decoder::ImageDimension::Dim1DArray ||
|
||||
requested == Decoder::ImageDimension::Dim2DArray;
|
||||
@@ -51,8 +51,7 @@ bool ValidImageDescriptor(const DescriptorValue& descriptor) {
|
||||
const auto base_level = (descriptor.dwords[3] >> 12u) & 0xfu;
|
||||
const auto fragments = (descriptor.dwords[3] >> 16u) & 0xfu;
|
||||
const auto max_mip = (descriptor.dwords[5] >> 4u) & 0xfu;
|
||||
return base_level == 0 && fragments >= 1 && fragments <= 3 &&
|
||||
max_mip == fragments;
|
||||
return base_level == 0 && fragments >= 1 && fragments <= 3 && max_mip == fragments;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -61,6 +60,11 @@ uint32_t DescriptorImageSwizzle(const DescriptorValue& descriptor) {
|
||||
return descriptor.dwords[3] & 0xfffu;
|
||||
}
|
||||
|
||||
bool DescriptorIsCube(const DescriptorValue& descriptor) {
|
||||
return static_cast<Prospero::ImageType>((descriptor.dwords[3] >> 28u) & 0xfu) ==
|
||||
Prospero::ImageType::kCube;
|
||||
}
|
||||
|
||||
bool DecodeBufferDescriptor(const DescriptorValue& descriptor, ShaderBufferResource& result) {
|
||||
if (descriptor.dword_count != std::size(result.fields)) {
|
||||
return false;
|
||||
@@ -171,12 +175,13 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
||||
const auto& image = program.info.images[i];
|
||||
const auto& descriptor = snapshot.images[i];
|
||||
if (NullImageDescriptor(descriptor)) {
|
||||
bool canonical_kind = image.kind == ResourceKind::Image ||
|
||||
image.kind == ResourceKind::StorageImage;
|
||||
bool canonical_kind =
|
||||
image.kind == ResourceKind::Image || image.kind == ResourceKind::StorageImage;
|
||||
if (image.atomic) {
|
||||
canonical_kind = image.kind == ResourceKind::StorageImageUint;
|
||||
}
|
||||
if (image.dimension != Decoder::ImageDimension::Dim2D || !canonical_kind) {
|
||||
if (image.dimension != Decoder::ImageDimension::Dim2D || image.cube ||
|
||||
!canonical_kind) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
"image descriptor {} no longer matches canonical null specialization", i);
|
||||
@@ -186,7 +191,8 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
||||
continue;
|
||||
}
|
||||
const auto dimension = DescriptorDimension(descriptor, image.dimension);
|
||||
if (dimension == Decoder::ImageDimension::Unknown || dimension != image.dimension) {
|
||||
if (dimension == Decoder::ImageDimension::Unknown || dimension != image.dimension ||
|
||||
DescriptorIsCube(descriptor) != image.cube) {
|
||||
if (error != nullptr) {
|
||||
*error =
|
||||
fmt::format("image descriptor {} no longer matches specialized dimension", i);
|
||||
@@ -361,6 +367,7 @@ bool SpecializeResources(Program& program, const ResourceSnapshot& snapshot, std
|
||||
auto& image = next.images[i];
|
||||
if (NullImageDescriptor(descriptor)) {
|
||||
image.dimension = Decoder::ImageDimension::Dim2D;
|
||||
image.cube = false;
|
||||
switch (image.kind) {
|
||||
case ResourceKind::ImageUint: image.kind = ResourceKind::Image; break;
|
||||
case ResourceKind::StorageImageUint:
|
||||
@@ -386,6 +393,7 @@ bool SpecializeResources(Program& program, const ResourceSnapshot& snapshot, std
|
||||
return false;
|
||||
}
|
||||
image.dimension = descriptor_dimension;
|
||||
image.cube = DescriptorIsCube(descriptor);
|
||||
if (image.kind == ResourceKind::StorageImage ||
|
||||
image.kind == ResourceKind::StorageImageUint) {
|
||||
image.storage_swizzle = DescriptorImageSwizzle(descriptor);
|
||||
@@ -402,6 +410,7 @@ bool SpecializeResources(Program& program, const ResourceSnapshot& snapshot, std
|
||||
std::reference_wrapper<Instruction> inst;
|
||||
ResourceKind kind;
|
||||
Decoder::ImageDimension dimension;
|
||||
bool cube;
|
||||
};
|
||||
std::vector<ImagePatch> patches;
|
||||
for (auto& block: program.blocks) {
|
||||
@@ -420,13 +429,14 @@ bool SpecializeResources(Program& program, const ResourceSnapshot& snapshot, std
|
||||
return false;
|
||||
}
|
||||
const auto& image = next.images[inst.memory.resource];
|
||||
patches.push_back({std::ref(inst), image.kind, image.dimension});
|
||||
patches.push_back({std::ref(inst), image.kind, image.dimension, image.cube});
|
||||
}
|
||||
}
|
||||
program.info = std::move(next);
|
||||
for (const auto& patch: patches) {
|
||||
patch.inst.get().memory.kind = patch.kind;
|
||||
patch.inst.get().memory.image_dimension = patch.dimension;
|
||||
patch.inst.get().memory.image_cube = patch.cube;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -432,6 +432,7 @@ struct MemoryInfo {
|
||||
bool typed = false;
|
||||
bool formatted = false;
|
||||
bool image_has_mip = false;
|
||||
bool image_cube = false;
|
||||
bool glc = false;
|
||||
bool slc = false;
|
||||
bool idxen = false;
|
||||
@@ -607,6 +608,7 @@ struct ImageResource {
|
||||
bool written = false;
|
||||
bool atomic = false;
|
||||
bool depth_compare = false;
|
||||
bool cube = false;
|
||||
|
||||
bool operator==(const ImageResource& other) const = default;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "common/abi.h"
|
||||
#include "libs/errno.h"
|
||||
#include "libs/libs.h"
|
||||
#include "loader/symbolDatabase.h"
|
||||
|
||||
namespace Libs {
|
||||
|
||||
LIB_VERSION("TextToSpeech2", 1, "TextToSpeech2", 1, 1);
|
||||
|
||||
namespace TextToSpeech2 {
|
||||
|
||||
static int KYTY_SYSV_ABI TextToSpeech2GetSpeechStatus() {
|
||||
PRINT_NAME();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI TextToSpeech2Cancel() {
|
||||
PRINT_NAME();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace TextToSpeech2
|
||||
|
||||
LIB_DEFINE(InitTextToSpeech2_1) {
|
||||
LIB_FUNC("08JSg9p6bgQ", TextToSpeech2::TextToSpeech2GetSpeechStatus);
|
||||
LIB_FUNC("2jiIxUmcsGo", TextToSpeech2::TextToSpeech2Cancel);
|
||||
}
|
||||
|
||||
} // namespace Libs
|
||||
@@ -66,6 +66,7 @@ LIB_DEFINE(InitSaveData_1);
|
||||
LIB_DEFINE(InitShare_1);
|
||||
LIB_DEFINE(InitSysmodule_1);
|
||||
LIB_DEFINE(InitSystemService_1);
|
||||
LIB_DEFINE(InitTextToSpeech2_1);
|
||||
LIB_DEFINE(InitUserService_1);
|
||||
LIB_DEFINE(InitVideoOut_1);
|
||||
|
||||
@@ -100,6 +101,7 @@ void InitAll(Loader::SymbolDatabase* s) {
|
||||
LIB_LOAD(InitShare_1);
|
||||
LIB_LOAD(InitSysmodule_1);
|
||||
LIB_LOAD(InitSystemService_1);
|
||||
LIB_LOAD(InitTextToSpeech2_1);
|
||||
LIB_LOAD(LibUlt::InitUlt_1);
|
||||
LIB_LOAD(InitUserService_1);
|
||||
LIB_LOAD(VideoDec2::InitVideoDec2_1);
|
||||
|
||||
@@ -1217,6 +1217,34 @@ void TestResourceSpecializationIsTypedAndTransactional() {
|
||||
Decoder::ImageDimension::Dim2DArray,
|
||||
"array MIMG intent did not produce a 2D-array view");
|
||||
|
||||
Program cube_view;
|
||||
cube_view.stage = ShaderType::Compute;
|
||||
cube_view.blocks.resize(1);
|
||||
cube_view.blocks[0].instructions = {
|
||||
ImageUse(0x24, Opcode::ImageLoad, ResourceKind::Image,
|
||||
Decoder::ImageDimension::Dim2DArray)};
|
||||
Prepare(cube_view);
|
||||
auto cube_snapshot = array_2d_snapshot;
|
||||
cube_snapshot.images[0].dwords[3] =
|
||||
Prospero::GpuEnumValue(Prospero::ImageType::kCube) << 28u;
|
||||
Check(SpecializeResources(cube_view, cube_snapshot, &error) &&
|
||||
ValidateResourceSpecialization(cube_view, cube_snapshot, &error) &&
|
||||
cube_view.info.images[0].cube &&
|
||||
cube_view.blocks[0].instructions[0].memory.image_cube,
|
||||
"cube descriptor identity did not reach the specialized image and IR");
|
||||
auto array_after_cube = cube_snapshot;
|
||||
array_after_cube.images[0].dwords[3] =
|
||||
Prospero::GpuEnumValue(Prospero::ImageType::kColor2DArray) << 28u;
|
||||
Check(!ValidateResourceSpecialization(cube_view, array_after_cube, &error),
|
||||
"2D-array descriptor reused a cube-coordinate specialization");
|
||||
auto null_after_cube = cube_snapshot;
|
||||
null_after_cube.images[0].dwords.fill(0);
|
||||
Check(SpecializeResources(cube_view, null_after_cube, &error) &&
|
||||
ValidateResourceSpecialization(cube_view, null_after_cube, &error) &&
|
||||
!cube_view.info.images[0].cube &&
|
||||
!cube_view.blocks[0].instructions[0].memory.image_cube,
|
||||
"canonical null respecialization retained stale cube-coordinate state");
|
||||
|
||||
Program program;
|
||||
program.stage = ShaderType::Compute;
|
||||
program.blocks.resize(1);
|
||||
|
||||
@@ -3020,6 +3020,37 @@ void TestNewShaderRecompilerImageQueryLowering() {
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerCubeSampleCoordinates() {
|
||||
constexpr uint32_t MimgDimCube = 3;
|
||||
const uint32_t shader[] = {
|
||||
EncodeMimg0(0x20, 0xf, false, MimgDimCube),
|
||||
EncodeMimg1(0, 0, 1, 0), // image_sample cube
|
||||
EncodeMimg0(0x60, 0x3, false, MimgDimCube),
|
||||
EncodeMimg1(8, 0, 1, 4), // image_get_lod cube
|
||||
0xbf810000u,
|
||||
};
|
||||
|
||||
auto user_data = ImageTestUserData(Prospero::ImageType::kCube);
|
||||
ShaderRecompiler::CompileOptions options;
|
||||
options.stage = ShaderType::Compute;
|
||||
options.user_data = user_data.data();
|
||||
|
||||
ShaderRecompiler::CompileResult result;
|
||||
std::string error;
|
||||
Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str());
|
||||
Check(result.program.info.images.size() == 1 && result.program.info.images[0].cube,
|
||||
"cube descriptor identity was not preserved through compilation");
|
||||
Check(SpirvInstructionOpcodeCount(result.spirv, 131) == 4,
|
||||
"cube sample/get-lod did not remove the RDNA2 S/T bias");
|
||||
Check(SpirvInstructionOpcodeCount(result.spirv, 109) == 1 &&
|
||||
SpirvInstructionOpcodeCount(result.spirv, 112) == 1 &&
|
||||
SpirvInstructionOpcodeCount(result.spirv, 194) == 1 &&
|
||||
SpirvInstructionOpcodeCount(result.spirv, 196) == 1 &&
|
||||
SpirvInstructionOpcodeCount(result.spirv, 130) == 1,
|
||||
"cube sample did not repack its face ID exactly once, or get-lod used an array layer");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerImageSampleVariants() {
|
||||
const uint32_t shader[] = {
|
||||
EncodeMimg0(0x24, 0xf),
|
||||
@@ -6957,6 +6988,7 @@ int main() {
|
||||
TestNewShaderRecompilerScalarBitfieldAlu();
|
||||
TestNewShaderRecompilerMemoryFamilyLowering();
|
||||
TestNewShaderRecompilerImageQueryLowering();
|
||||
TestNewShaderRecompilerCubeSampleCoordinates();
|
||||
TestNewShaderRecompilerImageSampleVariants();
|
||||
TestNewShaderRecompilerImageSampleA16SamplerCoords();
|
||||
TestNewShaderRecompilerImageSampleOpcodeAliases();
|
||||
|
||||
Reference in New Issue
Block a user