Compare commits

...
Sign in to create a new pull request.
Author SHA1 Message Date
CamilleLaVey
a8c6189d53 [vulkan] Restored Presync + ConditionalRendering workaround for QCOM drivers 2026-07-07 23:26:53 -04:00
CamilleLaVey
940cc8caef [vulkan] Add EAC formats on the query format table 2026-07-07 23:21:41 -04:00
CamilleLaVey
c57fdebdf0 [vic] Update logging for codec used 2026-07-07 22:27:19 -04:00
CamilleLaVey
059d902ca3 [android] Fix on missing transfer frames 2026-07-07 20:39:28 -04:00
CamilleLaVey
6df0089950 [vic] Quick change on VP9 offsets + ifdef encoding flags for Android 2026-07-07 20:13:56 -04:00
CamilleLaVey
d1f41a91ec [android] Verifying the actual flag required on hardware access 2026-07-07 20:08:10 -04:00
CamilleLaVey
a1b7cd51b4 [android] Initial implementation for MediaCodec 2026-07-07 19:01:42 -04:00
CamilleLaVey
6a3f885e46 [vulkan] 2D-Array (StorageView) mistmatch handling 2026-07-07 18:41:45 -04:00
CamilleLaVey
18a9d0762b Revert "[TEST] Return of early compute shader change" 2026-07-07 18:30:31 -04:00
CamilleLaVey
eb1060381a [TEST] Return of early compute shader change 2026-07-07 17:21:20 -04:00
CamilleLaVey
b270bae970 [TEST] Late catch on accelerated image upload path 2026-07-07 16:02:35 -04:00
CamilleLaVey
b54be98731 [TEST] Returned shaderFloat16 to QCOM 2026-07-07 15:49:29 -04:00
CamilleLaVey
0e1e0d5664 Fix build 2026-07-06 21:29:23 -04:00
CamilleLaVey
0b17ea1804 [TEST] Remove fp16 from qcom drivers 2026-07-06 20:47:56 -04:00
CamilleLaVey
06580cc6f9 [vulkan, qcom] Updated list of removed features on QCOM drivers 2026-07-06 19:55:19 -04:00
CamilleLaVey
bb63996182 [vulkan] Subgroups gating correction 2026-07-06 15:57:53 -04:00
CamilleLaVey
83e1a676c5 [vulkan, qcom] Removed border color swizzle from QCOM drivers 2026-07-06 03:28:53 -04:00
CamilleLaVey
21fa6b5805 [vulkan] Added checks for 8/16bits on storage buffer access 2026-07-06 03:26:03 -04:00
CamilleLaVey
62e8666eee Revert "[TEST] Debugger GPU unswizzle shaders" 2026-07-06 03:19:32 -04:00
CamilleLaVey
ca9ff1642f Revert "[TEST] Force checks on subgroups" 2026-07-06 03:16:54 -04:00
CamilleLaVey
ace66343ce [TEST] Force checks on subgroups 2026-07-06 03:07:48 -04:00
CamilleLaVey
4bf8828df1 [TEST] Debugger GPU unswizzle shaders 2026-07-06 02:10:21 -04:00
CamilleLaVey
7307518e44 [vulkan] Re-wiring older ASTC decoding path + new helper function on wider ASTC textures 2026-07-05 23:47:22 -04:00
CamilleLaVey
8890b8eb2a [TEST] Removal of the 7 push constant + fixated value 2026-07-05 23:07:57 -04:00
CamilleLaVey
a7d274e5bd [TEST] Adjusting sRGB formula 2026-07-05 20:46:33 -04:00
CamilleLaVey
bfdc50ee76 [TEST] Refactored SRGBToLinear function 2026-07-05 07:20:59 -04:00
CamilleLaVey
b0ccfb2c2a [TEST] Remove sRGB curve convertion 2026-07-05 07:10:41 -04:00
CamilleLaVey
f191d7a0b7 Revert "[TEST] Removing AMD/Nvidia from HDR on ASTC decoded formats" 2026-07-05 06:59:15 -04:00
CamilleLaVey
0a0156a484 [TEST] Removing AMD/Nvidia from HDR on ASTC decoded formats 2026-07-05 06:53:33 -04:00
CamilleLaVey
e48a8d3ff4 Revert "[TEST] Test for RefreshParametter actual concurrency" 2026-07-05 06:36:31 -04:00
CamilleLaVey
811ee522e0 Revert "[TEST] Diagnosis for corrupted HDR consumed views" 2026-07-05 06:36:06 -04:00
CamilleLaVey
7cb8ae0c0d [TEST] Diagnosis for corrupted HDR consumed views 2026-07-05 06:31:42 -04:00
CamilleLaVey
dbb48c4e4e [TEST] Format swap 2026-07-05 06:24:17 -04:00
CamilleLaVey
8cc658e5be [vulkan] Updated Typeless format structure for ASTC decoding 2026-07-05 05:26:47 -04:00
CamilleLaVey
785461a26c [vulkan] Verify actual format of ASTC before decode 2026-07-05 05:06:16 -04:00
CamilleLaVey
7bf0e49a91 [vulkan] Plumbing ASTC decoding capabilities for HDR 2026-07-05 03:45:32 -04:00
CamilleLaVey
55627c3896 [video_core] Initial implementation on HDR endpoints for ASTC decoding 2026-07-05 02:53:52 -04:00
CamilleLaVey
1987384dec [TEST] Test for RefreshParametter actual concurrency 2026-07-05 01:35:12 -04:00
CamilleLaVey
3b617a6733 [TEST] Add B10G11R11 as target format 2026-07-05 01:31:51 -04:00
CamilleLaVey
963e1f633b [TEST] Add preliminary/ missing formats based on driver reports for Switch 2026-07-05 00:36:26 -04:00
CamilleLaVey
6eec277428 [vulkan] Silence unimplemented format entry 2026-07-05 00:07:41 -04:00
CamilleLaVey
dbd59bd119 [vulkan] Override default behavior on rasterizer channel 2026-07-04 23:21:46 -04:00
CamilleLaVey
3dc15a4f96 Smolio Gidolardo 2026-07-04 22:10:02 -04:00
CamilleLaVey
4900fee536 [TEST] Re-test removed features from QCOM 2026-07-04 22:05:28 -04:00
CamilleLaVey
34df1b4a16 [vulkan] Another approach for queries reset 2026-07-04 22:03:22 -04:00
CamilleLaVey
f5560deab7 [vulkan] Added tracker for reused uploads 2026-07-04 21:41:00 -04:00
CamilleLaVey
307a32f9a0 [vulkan, qcom] Updated list of features removed for QCOM stock drivers 2026-07-04 20:44:25 -04:00
CamilleLaVey
7bb4991edd [vulkan] Refactored dual blending/ dual source attachments 2026-07-04 19:03:27 -04:00
CamilleLaVey
f5720663c2 [vulkan] Adjust blend targets from texture types 2026-07-04 18:55:00 -04:00
CamilleLaVey
53927e175e [vulkan] Adjustments on previous TFB barrier added 2026-07-04 18:26:29 -04:00
CamilleLaVey
20a68e1e9d [vulkan, qcom] Returned custom border color and derivates to QCOM drivers 2026-07-04 17:59:07 -04:00
CamilleLaVey
c7668fd43e [vulkan] Adjusted checks for 8 - 16bits UBO's iteration 2026-07-04 16:40:15 -04:00
CamilleLaVey
eb7f950322 [spir-v] Workaorund for 1D sampled images 2026-07-04 16:24:22 -04:00
CamilleLaVey
80609ee974 [TEST] Remove border color swizzle from qcom 2026-07-04 16:17:01 -04:00
CamilleLaVey
28630ff109 [vulkan] Sampled linear images fallback to nearest 2026-07-04 13:47:44 -04:00
CamilleLaVey
b049f7d273 [texture_cache] Relaxed TIC's lookup for integer type image formats 2026-07-04 12:57:20 -04:00
CamilleLaVey
f6ccf2c368 [shader_recompiler] Added texture type functions 2026-07-04 01:31:25 -04:00
CamilleLaVey
27a0010b6e [vulkan] Verify UniformAndStorage capabilities on device 2026-07-04 00:32:36 -04:00
CamilleLaVey
b06b9cc7a2 [vulkan] Pixel format handling for formats 2026-07-04 00:16:01 -04:00
CamilleLaVey
943f91cf0b [texture_cache] Added typeless storage form 2026-07-04 00:01:31 -04:00
CamilleLaVey
bd8caa5294 [texture_cache] Added b5g6r5 pixel format to depth blit resolves 2026-07-03 23:36:05 -04:00
CamilleLaVey
3ddf349ca1 [buffer_cache] Force offsets to 0 on descriptors and use VK_WHOLE_SIZE instead 2026-07-03 23:01:13 -04:00
CamilleLaVey
d707d241fc [host_shaders] Corrections on mismatchs for SINT on depth formats 2026-07-03 22:28:42 -04:00
CamilleLaVey
aa5d2cf9d7 [qcom] Removal custom border color from QCOM drivers 2026-07-03 22:10:12 -04:00
CamilleLaVey
decf41a3cb Fix build 2026-07-03 21:21:14 -04:00
CamilleLaVey
d7dc4f2143 [vulkan] Correction on mislead of BindVertexBuffer entries 2026-07-03 20:58:34 -04:00
CamilleLaVey
fbd94e4237 [vulkan] Implementation color write enable + color border swizzle 2026-07-03 20:35:45 -04:00
CamilleLaVey
48cc8f4c8c [vulkan] XFB memory barrier added 2026-07-03 02:29:06 -04:00
CamilleLaVey
e1721012d8 [vulkan] Set actual dynamic state handling for topology 2026-07-03 01:49:56 -04:00
CamilleLaVey
5af01bef9f [vulkan] Initial implementation Synchronization2 2026-07-03 00:58:40 -04:00
53 changed files with 1845 additions and 235 deletions

View file

@ -59,6 +59,11 @@ endif()
if (PLATFORM_PS4 OR PLATFORM_MANAGARM) if (PLATFORM_PS4 OR PLATFORM_MANAGARM)
# Doesn't support VA-API, don't go thru the embarrassment of trying to enable it # Doesn't support VA-API, don't go thru the embarrassment of trying to enable it
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi) list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi)
elseif (ANDROID)
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-mediacodec
--enable-jni
)
elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID) elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBVA libva) pkg_check_modules(LIBVA libva)

View file

@ -27,7 +27,7 @@ if (ARCHITECTURE_arm64)
target_link_libraries(yuzu-android PRIVATE adrenotools) target_link_libraries(yuzu-android PRIVATE adrenotools)
endif() endif()
target_link_libraries(yuzu-android PRIVATE OpenSSL::SSL cpp-jwt::cpp-jwt) target_link_libraries(yuzu-android PRIVATE ${FFmpeg_LIBRARIES} OpenSSL::SSL cpp-jwt::cpp-jwt)
if (ENABLE_UPDATE_CHECKER) if (ENABLE_UPDATE_CHECKER)
target_compile_definitions(yuzu-android PUBLIC ENABLE_UPDATE_CHECKER) target_compile_definitions(yuzu-android PUBLIC ENABLE_UPDATE_CHECKER)
endif() endif()

View file

@ -36,6 +36,10 @@
#include <frontend_common/content_manager.h> #include <frontend_common/content_manager.h>
#include <jni.h> #include <jni.h>
extern "C" {
#include <libavcodec/jni.h>
}
#include "common/android/multiplayer/multiplayer.h" #include "common/android/multiplayer/multiplayer.h"
#include "common/android/android_common.h" #include "common/android/android_common.h"
#include "common/android/id_cache.h" #include "common/android/id_cache.h"
@ -680,6 +684,13 @@ const char* fallback_cpu_detection() {
} // namespace } // namespace
extern "C" {
jint InitFFmpegOnLoad(JavaVM* vm) {
av_jni_set_java_vm(vm, nullptr);
return 0;
}
}
extern "C" { extern "C" {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance, void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance,

View file

@ -427,8 +427,11 @@ namespace Common::Android {
extern "C" { extern "C" {
#endif #endif
jint InitFFmpegOnLoad(JavaVM *vm);
jint JNI_OnLoad(JavaVM *vm, void *reserved) { jint JNI_OnLoad(JavaVM *vm, void *reserved) {
s_java_vm = vm; s_java_vm = vm;
InitFFmpegOnLoad(vm);
JNIEnv *env; JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION) != JNI_OK) if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION) != JNI_OK)

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#pragma once #pragma once

View file

@ -403,6 +403,9 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
if (info.uses_sampled_1d) { if (info.uses_sampled_1d) {
ctx.AddCapability(spv::Capability::Sampled1D); ctx.AddCapability(spv::Capability::Sampled1D);
} }
if (info.uses_image_1d) {
ctx.AddCapability(spv::Capability::Image1D);
}
if (info.uses_sparse_residency) { if (info.uses_sparse_residency) {
ctx.AddCapability(spv::Capability::SparseResidency); ctx.AddCapability(spv::Capability::SparseResidency);
} }
@ -432,7 +435,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
} }
if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id || if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id ||
info.uses_subgroup_shuffles) && info.uses_subgroup_shuffles) &&
profile.support_vote) { profile.support_vote && profile.SupportsSubgroupStage(ctx.stage)) {
ctx.AddCapability(spv::Capability::GroupNonUniformBallot); ctx.AddCapability(spv::Capability::GroupNonUniformBallot);
ctx.AddCapability(spv::Capability::GroupNonUniformShuffle); ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
if (!profile.warp_size_potentially_larger_than_guest) { if (!profile.warp_size_potentially_larger_than_guest) {

View file

@ -202,7 +202,8 @@ void EmitGetIndirectBranchVariable(EmitContext&) {
} }
Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8 &&
ctx.profile.support_uniform_and_storage_buffer_8bit) {
const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset, const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset,
ctx.load_const_func_u8)}; ctx.load_const_func_u8)};
return ctx.OpUConvert(ctx.U32[1], load); return ctx.OpUConvert(ctx.U32[1], load);
@ -219,7 +220,8 @@ Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of
} }
Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8 &&
ctx.profile.support_uniform_and_storage_buffer_8bit) {
const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset, const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset,
ctx.load_const_func_u8)}; ctx.load_const_func_u8)};
return ctx.OpSConvert(ctx.U32[1], load); return ctx.OpSConvert(ctx.U32[1], load);
@ -236,7 +238,8 @@ Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of
} }
Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) { if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16 &&
ctx.profile.support_uniform_and_storage_buffer_16bit) {
const Id load{GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset, const Id load{GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset,
ctx.load_const_func_u16)}; ctx.load_const_func_u16)};
return ctx.OpUConvert(ctx.U32[1], load); return ctx.OpUConvert(ctx.U32[1], load);
@ -253,7 +256,8 @@ Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
} }
Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) { if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16 &&
ctx.profile.support_uniform_and_storage_buffer_16bit) {
const Id load{GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset, const Id load{GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset,
ctx.load_const_func_u16)}; ctx.load_const_func_u16)};
return ctx.OpSConvert(ctx.U32[1], load); return ctx.OpSConvert(ctx.U32[1], load);

View file

@ -260,6 +260,13 @@ bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
return ctx.textures.at(info.descriptor_index).is_multisample; return ctx.textures.at(info.descriptor_index).is_multisample;
} }
bool IsTextureInteger(EmitContext& ctx, const IR::TextureInstInfo& info) {
if (info.type == TextureType::Buffer) {
return false;
}
return ctx.textures.at(info.descriptor_index).is_integer;
}
Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) { Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
if (info.relaxed_precision != 0) { if (info.relaxed_precision != 0) {
@ -480,11 +487,14 @@ Id EmitBoundImageWrite(EmitContext&) {
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id bias_lc, const IR::Value& offset) { Id bias_lc, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const bool is_integer{IsTextureInteger(ctx, info)};
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
Id color;
if (ctx.stage == Stage::Fragment) { if (ctx.stage == Stage::Fragment) {
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0, const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
bias_lc, offset); bias_lc, offset);
return Emit(&EmitContext::OpImageSparseSampleImplicitLod, color = Emit(&EmitContext::OpImageSparseSampleImplicitLod,
&EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4], &EmitContext::OpImageSampleImplicitLod, ctx, inst, result_type,
Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
} else { } else {
// We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as // We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as
@ -492,26 +502,29 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
// derivatives // derivatives
const Id lod{ctx.Const(0.0f)}; const Id lod{ctx.Const(0.0f)};
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset); const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
return Emit(&EmitContext::OpImageSparseSampleExplicitLod, color = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], &EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type,
Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
} }
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
} }
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id lod, const IR::Value& offset) { Id lod, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const bool is_integer{IsTextureInteger(ctx, info)};
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
const ImageOperands operands(ctx, false, true, false, lod, offset); const ImageOperands operands(ctx, false, true, false, lod, offset);
Id result = Emit(&EmitContext::OpImageSparseSampleExplicitLod, Id result = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], &EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type,
Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
#ifdef __ANDROID__ #ifdef __ANDROID__
if (Settings::values.fix_bloom_effects.GetValue()) { if (!is_integer && Settings::values.fix_bloom_effects.GetValue()) {
result = ctx.OpVectorTimesScalar(ctx.F32[4], result, ctx.Const(0.98f)); result = ctx.OpVectorTimesScalar(ctx.F32[4], result, ctx.Const(0.98f));
} }
#endif #endif
return result; return is_integer ? ctx.OpBitcast(ctx.F32[4], result) : result;
} }
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
@ -547,30 +560,39 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2) { const IR::Value& offset, const IR::Value& offset2) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const bool is_integer{IsTextureInteger(ctx, info)};
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
const ImageOperands operands(ctx, offset, offset2); const ImageOperands operands(ctx, offset, offset2);
if (ctx.profile.need_gather_subpixel_offset) { if (ctx.profile.need_gather_subpixel_offset) {
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords); coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
} }
return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst, const Id color{Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component), result_type, Texture(ctx, info, index), coords,
operands.MaskOptional(), operands.Span()); ctx.Const(info.gather_component), operands.MaskOptional(),
operands.Span())};
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
} }
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2, Id dref) { const IR::Value& offset, const IR::Value& offset2, Id dref) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const bool is_integer{IsTextureInteger(ctx, info)};
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
const ImageOperands operands(ctx, offset, offset2); const ImageOperands operands(ctx, offset, offset2);
if (ctx.profile.need_gather_subpixel_offset) { if (ctx.profile.need_gather_subpixel_offset) {
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords); coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
} }
return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst, const Id color{Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather,
ctx.F32[4], Texture(ctx, info, index), coords, dref, operands.MaskOptional(), ctx, inst, result_type, Texture(ctx, info, index), coords, dref,
operands.Span()); operands.MaskOptional(), operands.Span())};
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
} }
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
Id lod, Id ms) { Id lod, Id ms) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const bool is_integer{IsTextureInteger(ctx, info)};
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
AddOffsetToCoordinates(ctx, info, coords, offset); AddOffsetToCoordinates(ctx, info, coords, offset);
if (info.type == TextureType::Buffer) { if (info.type == TextureType::Buffer) {
lod = Id{}; lod = Id{};
@ -580,8 +602,10 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
lod = Id{}; lod = Id{};
} }
const ImageOperands operands(lod, ms); const ImageOperands operands(lod, ms);
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], const Id color{Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst,
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); result_type, TextureImage(ctx, info, index), coords,
operands.MaskOptional(), operands.Span())};
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
} }
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod, Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
@ -626,14 +650,17 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id derivatives, const IR::Value& offset, Id lod_clamp) { Id derivatives, const IR::Value& offset, Id lod_clamp) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const bool is_integer{IsTextureInteger(ctx, info)};
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
const auto operands = info.num_derivatives == 3 const auto operands = info.num_derivatives == 3
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, ? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
ctx.Def(offset), {}, lod_clamp) ctx.Def(offset), {}, lod_clamp)
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, : ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
info.num_derivatives, offset, lod_clamp); info.num_derivatives, offset, lod_clamp);
return Emit(&EmitContext::OpImageSparseSampleExplicitLod, const Id color{Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], &EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type,
Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); Texture(ctx, info, index), coords, operands.Mask(), operands.Span())};
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
} }
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) { Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -10,7 +13,14 @@ Id SubgroupScope(EmitContext& ctx) {
return ctx.Const(static_cast<u32>(spv::Scope::Subgroup)); return ctx.Const(static_cast<u32>(spv::Scope::Subgroup));
} }
bool StageSupportsSubgroups(EmitContext& ctx) {
return ctx.profile.SupportsSubgroupStage(ctx.stage);
}
Id GetThreadId(EmitContext& ctx) { Id GetThreadId(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.u32_zero_value;
}
return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id); return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
} }
@ -68,6 +78,9 @@ Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask
} }
Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) { Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
if (!StageSupportsSubgroups(ctx)) {
return value;
}
return ctx.OpSelect( return ctx.OpSelect(
ctx.U32[1], in_range, ctx.U32[1], in_range,
ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value); ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value);
@ -89,6 +102,9 @@ Id EmitLaneId(EmitContext& ctx) {
} }
Id EmitVoteAll(EmitContext& ctx, Id pred) { Id EmitVoteAll(EmitContext& ctx, Id pred) {
if (!StageSupportsSubgroups(ctx)) {
return pred;
}
if (!ctx.profile.warp_size_potentially_larger_than_guest) { if (!ctx.profile.warp_size_potentially_larger_than_guest) {
return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred); return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred);
} }
@ -102,6 +118,9 @@ Id EmitVoteAll(EmitContext& ctx, Id pred) {
} }
Id EmitVoteAny(EmitContext& ctx, Id pred) { Id EmitVoteAny(EmitContext& ctx, Id pred) {
if (!StageSupportsSubgroups(ctx)) {
return pred;
}
if (!ctx.profile.warp_size_potentially_larger_than_guest) { if (!ctx.profile.warp_size_potentially_larger_than_guest) {
return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred); return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred);
} }
@ -115,6 +134,9 @@ Id EmitVoteAny(EmitContext& ctx, Id pred) {
} }
Id EmitVoteEqual(EmitContext& ctx, Id pred) { Id EmitVoteEqual(EmitContext& ctx, Id pred) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.true_value;
}
if (!ctx.profile.warp_size_potentially_larger_than_guest) { if (!ctx.profile.warp_size_potentially_larger_than_guest) {
return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred); return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred);
} }
@ -129,6 +151,9 @@ Id EmitVoteEqual(EmitContext& ctx, Id pred) {
} }
Id EmitSubgroupBallot(EmitContext& ctx, Id pred) { Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.OpSelect(ctx.U32[1], pred, ctx.Const(1u), ctx.u32_zero_value);
}
const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)}; const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)};
if (!ctx.profile.warp_size_potentially_larger_than_guest) { if (!ctx.profile.warp_size_potentially_larger_than_guest) {
return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U); return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
@ -137,22 +162,37 @@ Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
} }
Id EmitSubgroupEqMask(EmitContext& ctx) { Id EmitSubgroupEqMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.Const(1u);
}
return LoadMask(ctx, ctx.subgroup_mask_eq); return LoadMask(ctx, ctx.subgroup_mask_eq);
} }
Id EmitSubgroupLtMask(EmitContext& ctx) { Id EmitSubgroupLtMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.u32_zero_value;
}
return LoadMask(ctx, ctx.subgroup_mask_lt); return LoadMask(ctx, ctx.subgroup_mask_lt);
} }
Id EmitSubgroupLeMask(EmitContext& ctx) { Id EmitSubgroupLeMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.Const(1u);
}
return LoadMask(ctx, ctx.subgroup_mask_le); return LoadMask(ctx, ctx.subgroup_mask_le);
} }
Id EmitSubgroupGtMask(EmitContext& ctx) { Id EmitSubgroupGtMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.u32_zero_value;
}
return LoadMask(ctx, ctx.subgroup_mask_gt); return LoadMask(ctx, ctx.subgroup_mask_gt);
} }
Id EmitSubgroupGeMask(EmitContext& ctx) { Id EmitSubgroupGeMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.Const(1u);
}
return LoadMask(ctx, ctx.subgroup_mask_ge); return LoadMask(ctx, ctx.subgroup_mask_ge);
} }
@ -222,7 +262,7 @@ Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) { Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) {
const Id three{ctx.Const(3U)}; const Id three{ctx.Const(3U)};
Id mask{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; Id mask{GetThreadId(ctx)};
mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three); mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three);
mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U)); mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U));
mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask); mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask);

View file

@ -30,7 +30,7 @@ enum class Operation {
Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) { Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
const spv::ImageFormat format{spv::ImageFormat::Unknown}; const spv::ImageFormat format{spv::ImageFormat::Unknown};
const Id type{ctx.F32[1]}; const Id type{desc.is_integer ? ctx.U32[1] : ctx.F32[1]};
const bool depth{desc.is_depth}; const bool depth{desc.is_depth};
const bool ms{desc.is_multisample}; const bool ms{desc.is_multisample};
switch (desc.type) { switch (desc.type) {
@ -1126,7 +1126,7 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
} }
IR::Type types{info.used_constant_buffer_types | info.used_indirect_cbuf_types}; IR::Type types{info.used_constant_buffer_types | info.used_indirect_cbuf_types};
if (True(types & IR::Type::U8)) { if (True(types & IR::Type::U8)) {
if (profile.support_int8) { if (profile.support_int8 && profile.support_uniform_and_storage_buffer_8bit) {
DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8)); DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8));
DefineConstBuffers(*this, info, &UniformDefinitions::S8, binding, S8, 's', sizeof(s8)); DefineConstBuffers(*this, info, &UniformDefinitions::S8, binding, S8, 's', sizeof(s8));
} else { } else {
@ -1134,7 +1134,7 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
} }
} }
if (True(types & IR::Type::U16)) { if (True(types & IR::Type::U16)) {
if (profile.support_int16) { if (profile.support_int16 && profile.support_uniform_and_storage_buffer_16bit) {
DefineConstBuffers(*this, info, &UniformDefinitions::U16, binding, U16, 'u', DefineConstBuffers(*this, info, &UniformDefinitions::U16, binding, U16, 'u',
sizeof(u16)); sizeof(u16));
DefineConstBuffers(*this, info, &UniformDefinitions::S16, binding, S16, 's', DefineConstBuffers(*this, info, &UniformDefinitions::S16, binding, S16, 's',
@ -1196,10 +1196,18 @@ void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) {
IR::Type types{info.used_indirect_cbuf_types}; IR::Type types{info.used_indirect_cbuf_types};
bool supports_aliasing = profile.support_descriptor_aliasing; bool supports_aliasing = profile.support_descriptor_aliasing;
if (supports_aliasing && True(types & IR::Type::U8)) { if (supports_aliasing && True(types & IR::Type::U8)) {
load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8); if (profile.support_int8 && profile.support_uniform_and_storage_buffer_8bit) {
load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8);
} else {
types |= IR::Type::U32;
}
} }
if (supports_aliasing && True(types & IR::Type::U16)) { if (supports_aliasing && True(types & IR::Type::U16)) {
load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16); if (profile.support_int16 && profile.support_uniform_and_storage_buffer_16bit) {
load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16);
} else {
types |= IR::Type::U32;
}
} }
if (supports_aliasing && True(types & IR::Type::F32)) { if (supports_aliasing && True(types & IR::Type::F32)) {
load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32); load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32);
@ -1375,6 +1383,7 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in
.image_type = image_type, .image_type = image_type,
.count = desc.count, .count = desc.count,
.is_multisample = desc.is_multisample, .is_multisample = desc.is_multisample,
.is_integer = desc.is_integer,
}); });
if (profile.supported_spirv >= 0x00010400) { if (profile.supported_spirv >= 0x00010400) {
interfaces.push_back(id); interfaces.push_back(id);
@ -1438,7 +1447,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
if (info.uses_is_helper_invocation) { if (info.uses_is_helper_invocation) {
is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation); is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
} }
if (info.uses_subgroup_mask) { if (info.uses_subgroup_mask && profile.SupportsSubgroupStage(stage)) {
subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR); subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR);
subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR); subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR);
subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR); subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
@ -1452,9 +1461,10 @@ void EmitContext::DefineInputs(const IR::Program& program) {
Decorate(subgroup_mask_ge, spv::Decoration::Flat); Decorate(subgroup_mask_ge, spv::Decoration::Flat);
} }
} }
if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || if ((info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
(profile.warp_size_potentially_larger_than_guest && (profile.warp_size_potentially_larger_than_guest &&
(info.uses_subgroup_vote || info.uses_subgroup_mask))) { (info.uses_subgroup_vote || info.uses_subgroup_mask))) &&
profile.SupportsSubgroupStage(stage)) {
AddCapability(spv::Capability::GroupNonUniform); AddCapability(spv::Capability::GroupNonUniform);
subgroup_local_invocation_id = subgroup_local_invocation_id =
DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId);

View file

@ -42,6 +42,7 @@ struct TextureDefinition {
Id image_type; Id image_type;
u32 count; u32 count;
bool is_multisample; bool is_multisample;
bool is_integer;
}; };
struct TextureBufferDefinition { struct TextureBufferDefinition {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -43,6 +46,10 @@ union TextureInstInfo {
BitField<25, 2, u32> num_derivatives; BitField<25, 2, u32> num_derivatives;
BitField<27, 3, ImageFormat> image_format; BitField<27, 3, ImageFormat> image_format;
BitField<30, 1, u32> ndv_is_active; BitField<30, 1, u32> ndv_is_active;
/// Only meaningful for the sampled-texture family (ImageSampleImplicitLod, ImageFetch,
/// ImageGather, etc.); unused/zero for storage-image opcodes, which carry their own
/// is_integer via ImageDescriptor/ImageBufferDescriptor instead.
BitField<31, 1, u32> is_integer;
}; };
static_assert(sizeof(TextureInstInfo) <= sizeof(u32)); static_assert(sizeof(TextureInstInfo) <= sizeof(u32));

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@ -569,6 +569,8 @@ void VisitUsages(Info& info, IR::Inst& inst) {
case IR::Opcode::ImageRead: { case IR::Opcode::ImageRead: {
const auto flags{inst.Flags<IR::TextureInstInfo>()}; const auto flags{inst.Flags<IR::TextureInstInfo>()};
info.uses_typeless_image_reads |= flags.image_format == ImageFormat::Typeless; info.uses_typeless_image_reads |= flags.image_format == ImageFormat::Typeless;
info.uses_image_1d |=
flags.type == TextureType::Color1D || flags.type == TextureType::ColorArray1D;
info.uses_sparse_residency |= info.uses_sparse_residency |=
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr; inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
break; break;
@ -577,6 +579,8 @@ void VisitUsages(Info& info, IR::Inst& inst) {
const auto flags{inst.Flags<IR::TextureInstInfo>()}; const auto flags{inst.Flags<IR::TextureInstInfo>()};
info.uses_typeless_image_writes |= flags.image_format == ImageFormat::Typeless; info.uses_typeless_image_writes |= flags.image_format == ImageFormat::Typeless;
info.uses_image_buffers |= flags.type == TextureType::Buffer; info.uses_image_buffers |= flags.type == TextureType::Buffer;
info.uses_image_1d |=
flags.type == TextureType::Color1D || flags.type == TextureType::ColorArray1D;
break; break;
} }
case IR::Opcode::SubgroupEqMask: case IR::Opcode::SubgroupEqMask:
@ -761,9 +765,13 @@ void VisitUsages(Info& info, IR::Inst& inst) {
case IR::Opcode::ImageAtomicAnd32: case IR::Opcode::ImageAtomicAnd32:
case IR::Opcode::ImageAtomicOr32: case IR::Opcode::ImageAtomicOr32:
case IR::Opcode::ImageAtomicXor32: case IR::Opcode::ImageAtomicXor32:
case IR::Opcode::ImageAtomicExchange32: case IR::Opcode::ImageAtomicExchange32: {
const auto flags{inst.Flags<IR::TextureInstInfo>()};
info.uses_atomic_image_u32 = true; info.uses_atomic_image_u32 = true;
info.uses_image_1d |=
flags.type == TextureType::Color1D || flags.type == TextureType::ColorArray1D;
break; break;
}
default: default:
break; break;
} }

View file

@ -562,6 +562,7 @@ public:
})}; })};
// TODO: Read this from TIC // TODO: Read this from TIC
texture_descriptors[index].is_multisample |= desc.is_multisample; texture_descriptors[index].is_multisample |= desc.is_multisample;
texture_descriptors[index].is_integer |= desc.is_integer;
return index; return index;
} }
@ -685,6 +686,21 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
program.info.image_descriptors, program.info.image_descriptors,
}; };
const u32 sampled_dynamic_cap = DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace)); const u32 sampled_dynamic_cap = DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace));
bool has_last_is_integer{false};
u32 last_cbuf_index{};
u32 last_cbuf_offset{};
bool last_is_integer{false};
const auto is_texture_pixel_format_integer{[&](const ConstBufferAddr& cbuf_addr) {
if (has_last_is_integer && last_cbuf_index == cbuf_addr.index &&
last_cbuf_offset == cbuf_addr.offset) {
return last_is_integer;
}
last_is_integer = IsTexturePixelFormatIntegerCached(env, cbuf_addr);
last_cbuf_index = cbuf_addr.index;
last_cbuf_offset = cbuf_addr.offset;
has_last_is_integer = true;
return last_is_integer;
}};
for (TextureInst& texture_inst : to_replace) { for (TextureInst& texture_inst : to_replace) {
// TODO: Handle arrays // TODO: Handle arrays
IR::Inst* const inst{texture_inst.inst}; IR::Inst* const inst{texture_inst.inst};
@ -749,7 +765,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
} }
const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead}; const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead};
const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite}; const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite};
const bool is_integer{IsTexturePixelFormatIntegerCached(env, cbuf)}; const bool is_integer{is_texture_pixel_format_integer(cbuf)};
if (flags.type == TextureType::Buffer) { if (flags.type == TextureType::Buffer) {
index = descriptors.Add(ImageBufferDescriptor{ index = descriptors.Add(ImageBufferDescriptor{
.format = flags.image_format, .format = flags.image_format,
@ -791,10 +807,12 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
}); });
} else { } else {
count = std::min(count, sampled_dynamic_cap); count = std::min(count, sampled_dynamic_cap);
const bool is_integer{is_texture_pixel_format_integer(cbuf)};
index = descriptors.Add(TextureDescriptor{ index = descriptors.Add(TextureDescriptor{
.type = flags.type, .type = flags.type,
.is_depth = flags.is_depth != 0, .is_depth = flags.is_depth != 0,
.is_multisample = is_multisample, .is_multisample = is_multisample,
.is_integer = is_integer,
.has_secondary = cbuf.has_secondary, .has_secondary = cbuf.has_secondary,
.cbuf_index = cbuf.index, .cbuf_index = cbuf.index,
.cbuf_offset = cbuf.offset, .cbuf_offset = cbuf.offset,
@ -805,6 +823,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
.count = count, .count = count,
.size_shift = size_shift, .size_shift = size_shift,
}); });
flags.is_integer.Assign(is_integer ? 1 : 0);
} }
break; break;
} }

View file

@ -10,12 +10,16 @@
namespace Shader { namespace Shader {
enum class Stage : u32;
struct Profile { struct Profile {
u32 supported_spirv{0x00010000}; u32 supported_spirv{0x00010000};
bool unified_descriptor_binding{}; bool unified_descriptor_binding{};
bool support_descriptor_aliasing{}; bool support_descriptor_aliasing{};
bool support_int8{}; bool support_int8{};
bool support_uniform_and_storage_buffer_8bit{};
bool support_int16{}; bool support_int16{};
bool support_uniform_and_storage_buffer_16bit{};
bool support_int64{}; bool support_int64{};
bool support_vertex_instance_id{}; bool support_vertex_instance_id{};
bool support_float_controls{}; bool support_float_controls{};
@ -30,6 +34,7 @@ struct Profile {
bool support_fp64_signed_zero_nan_preserve{}; bool support_fp64_signed_zero_nan_preserve{};
bool support_explicit_workgroup_layout{}; bool support_explicit_workgroup_layout{};
bool support_vote{}; bool support_vote{};
u32 supported_subgroup_stages{0x7F};
bool support_viewport_index_layer_non_geometry{}; bool support_viewport_index_layer_non_geometry{};
bool support_viewport_mask{}; bool support_viewport_mask{};
bool support_typeless_image_loads{}; bool support_typeless_image_loads{};
@ -93,6 +98,10 @@ struct Profile {
u64 min_ssbo_alignment{}; u64 min_ssbo_alignment{};
u32 max_user_clip_distances{}; u32 max_user_clip_distances{};
bool SupportsSubgroupStage(Stage stage) const {
return (supported_subgroup_stages & (1u << static_cast<u32>(stage))) != 0;
}
}; };
} // namespace Shader } // namespace Shader

View file

@ -209,6 +209,7 @@ struct TextureDescriptor {
TextureType type; TextureType type;
bool is_depth; bool is_depth;
bool is_multisample; bool is_multisample;
bool is_integer;
bool has_secondary; bool has_secondary;
u32 cbuf_index; u32 cbuf_index;
u32 cbuf_offset; u32 cbuf_offset;

View file

@ -20,58 +20,54 @@ Decoder::Decoder(Host1x::Host1x& host1x_, s32 id_, const Host1x::NvdecCommon::Nv
Decoder::~Decoder() = default; Decoder::~Decoder() = default;
void Decoder::SetFrameDimensions(s32 width, s32 height) {
if (width <= 0 || height <= 0) {
frame_dimensions.reset();
return;
}
frame_dimensions = FFmpeg::FrameDimensions{width, height};
}
void Decoder::Decode() { void Decoder::Decode() {
if (!initialized) { if (!initialized) {
return; return;
} }
const auto packet_data = ComposeFrame(); const auto packet_data = ComposeFrame();
// Send assembled bitstream to decoder. FFmpeg::FrameOffsets offsets{};
if (!decode_api.SendPacket(packet_data)) { offsets.hidden = vp9_hidden_frame;
return; offsets.interlaced = IsInterlaced();
} if (offsets.interlaced) {
std::tie(offsets.luma, offsets.luma_bottom, std::ignore, std::ignore) =
// Only receive/store visible frames. GetInterlacedOffsets();
if (vp9_hidden_frame) {
return;
}
// Receive output frames from decoder.
auto frame = decode_api.ReceiveFrame();
if (!frame) {
return;
}
if (IsInterlaced()) {
auto [luma_top, luma_bottom, chroma_top, chroma_bottom] = GetInterlacedOffsets();
auto frame_copy = frame;
if (!frame.get()) {
LOG_ERROR(HW_GPU,
"Nvdec {} failed to decode interlaced frame for top {:#X} bottom 0x{:X}", id,
luma_top, luma_bottom);
}
if (UsingDecodeOrder()) {
host1x.frame_queue.PushDecodeOrder(id, luma_top, std::move(frame));
host1x.frame_queue.PushDecodeOrder(id, luma_bottom, std::move(frame_copy));
} else {
host1x.frame_queue.PushPresentOrder(id, luma_top, std::move(frame));
host1x.frame_queue.PushPresentOrder(id, luma_bottom, std::move(frame_copy));
}
} else { } else {
auto [luma_offset, chroma_offset] = GetProgressiveOffsets(); std::tie(offsets.luma, std::ignore) = GetProgressiveOffsets();
}
if (!frame.get()) { // Send assembled bitstream to decoder.
LOG_ERROR(HW_GPU, "Nvdec {} failed to decode progressive frame for luma {:#X}", id, if (!decode_api.SendPacket(packet_data, offsets, GetFrameDimensions())) {
luma_offset); return;
} }
auto push = [&](u64 luma, std::shared_ptr<FFmpeg::Frame> frame) {
if (UsingDecodeOrder()) { if (UsingDecodeOrder()) {
host1x.frame_queue.PushDecodeOrder(id, luma_offset, std::move(frame)); host1x.frame_queue.PushDecodeOrder(id, luma, std::move(frame));
} else { } else {
host1x.frame_queue.PushPresentOrder(id, luma_offset, std::move(frame)); host1x.frame_queue.PushPresentOrder(id, luma, std::move(frame));
}
};
while (auto result = decode_api.ReceiveFrame()) {
auto& [frame, frame_offsets] = *result;
if (!frame) {
continue;
}
if (frame_offsets.interlaced) {
auto frame_copy = frame;
push(frame_offsets.luma, std::move(frame));
push(frame_offsets.luma_bottom, std::move(frame_copy));
} else {
push(frame_offsets.luma, std::move(frame));
} }
} }
} }

View file

@ -10,6 +10,7 @@
#include <mutex> #include <mutex>
#include <optional> #include <optional>
#include <string_view> #include <string_view>
#include <tuple>
#include <ankerl/unordered_dense.h> #include <ankerl/unordered_dense.h>
#include <queue> #include <queue>
@ -46,12 +47,19 @@ protected:
virtual std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() = 0; virtual std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() = 0;
virtual bool IsInterlaced() = 0; virtual bool IsInterlaced() = 0;
void SetFrameDimensions(s32 width, s32 height);
std::optional<FFmpeg::FrameDimensions> GetFrameDimensions() const {
return frame_dimensions;
}
FFmpeg::DecodeApi decode_api; FFmpeg::DecodeApi decode_api;
Host1x::Host1x& host1x; Host1x::Host1x& host1x;
const Host1x::NvdecCommon::NvdecRegisters& regs; const Host1x::NvdecCommon::NvdecRegisters& regs;
s32 id; s32 id;
bool initialized : 1 = false; bool initialized : 1 = false;
bool vp9_hidden_frame : 1 = false; bool vp9_hidden_frame : 1 = false;
std::optional<FFmpeg::FrameDimensions> frame_dimensions;
}; };
} // namespace Tegra } // namespace Tegra

View file

@ -52,6 +52,10 @@ bool H264::IsInterlaced() {
std::span<const u8> H264::ComposeFrame() { std::span<const u8> H264::ComposeFrame() {
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(H264DecoderContext)); host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(H264DecoderContext));
const auto& params = current_context.h264_parameter_set;
SetFrameDimensions(static_cast<s32>(params.pic_width_in_mbs) * 16,
static_cast<s32>(params.frame_height_in_mbs) * 16);
const s64 frame_number = current_context.h264_parameter_set.frame_number.Value(); const s64 frame_number = current_context.h264_parameter_set.frame_number.Value();
if (!is_first_frame && frame_number != 0) { if (!is_first_frame && frame_number != 0) {
frame_scratch.resize_destructive(current_context.stream_len); frame_scratch.resize_destructive(current_context.stream_len);

View file

@ -35,6 +35,7 @@ std::tuple<u64, u64, u64, u64> VP8::GetInterlacedOffsets() {
std::span<const u8> VP8::ComposeFrame() { std::span<const u8> VP8::ComposeFrame() {
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(VP8PictureInfo)); host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(VP8PictureInfo));
SetFrameDimensions(current_context.frame_width, current_context.frame_height);
const bool is_key_frame = current_context.key_frame == 1u; const bool is_key_frame = current_context.key_frame == 1u;
const auto bitstream_size = size_t(current_context.vld_buffer_size); const auto bitstream_size = size_t(current_context.vld_buffer_size);

Some files were not shown because too many files have changed in this diff Show more