Browse Source

Added hack setting to partial fix shadow boxes in MP4

pull/3300/head
Forrest Keller 2 months ago
parent
commit
0ec2b0e9ff
  1. 1
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
  2. 7
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
  3. 1
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
  4. 3
      src/android/app/src/main/res/values/strings.xml
  5. 2
      src/common/settings.h
  6. 4
      src/qt_common/config/shared_translation.cpp
  7. 35
      src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
  8. 49
      src/video_core/texture_cache/texture_cache.h

1
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt

@ -39,6 +39,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
USE_CUSTOM_RTC("custom_rtc_enabled"),
BLACK_BACKGROUNDS("black_backgrounds"),
INVERT_CONFIRM_BACK_CONTROLLER_BUTTONS("invert_confirm_back_controller_buttons"),
HACK_FIX_SHADOWARRAY("fix_shadow_array_handles"),
ENABLE_FOLDER_BUTTON("enable_folder_button"),
ENABLE_QLAUNCH_BUTTON("enable_qlaunch_button"),

7
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt

@ -720,6 +720,13 @@ abstract class SettingsItem(
descriptionId = R.string.gpu_unswizzle_chunk_size_description,
choicesId = R.array.gpuSwizzleChunkEntries,
valuesId = R.array.gpuSwizzleChunkValues
)
)
put(
SwitchSetting(
BooleanSetting.HACK_FIX_SHADOWARRAY,
titleId = R.string.hack_fix_shadowarray,
descriptionId = R.string.hack_fix_shadowarray_description
)
)
put(

1
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt

@ -287,6 +287,7 @@ class SettingsFragmentPresenter(
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
add(SettingsItem.GPU_UNSWIZZLE_COMBINED)
add(BooleanSetting.HACK_FIX_SHADOWARRAY.key)
add(HeaderSetting(R.string.extensions))

3
src/android/app/src/main/res/values/strings.xml

@ -524,7 +524,8 @@
<string name="gpu_unswizzle_chunk_size">GPU Unswizzle Chunk Size</string>
<string name="gpu_unswizzle_chunk_size_description">Defines the number of depth slices processed per batch for 3D textures. Increasing this improves throughput efficiency on powerful GPUs but may cause stuttering or driver timeouts on weaker hardware.</string>
<string name="gpu_unswizzle_default_button">Default</string>
<string name="hack_fix_shadowarray">Fix Shadow Array Handles [EXPERIMENTAL]</string>
<string name="hack_fix_shadowarray_description">Reuse the last valid texture handle within a descriptor array if a slot is null. Fixes black shadows or missing textures in games that rely on array broadcasting (e.g., Cascaded Shadow Maps).</string>
<string name="extensions">Extensions</string>

2
src/common/settings.h

@ -580,6 +580,8 @@ struct Values {
"gpu_unswizzle_chunk_size",
Category::RendererHacks,
Specialization::Default};
SwitchableSetting<bool> hack_fix_shadowarray{linkage, false, "hack_fix_shadowarray",
Category::RendererHacks};
SwitchableSetting<bool> gpu_unswizzle_enabled{linkage, false, "gpu_unswizzle_enabled",
Category::RendererHacks};

4
src/qt_common/config/shared_translation.cpp

@ -318,6 +318,10 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent)
tr("GPU Unswizzle Chunk Size"),
tr("Determines the number of depth slices processed in a single dispatch.\n"
"Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware."));
INSERT(Settings,
hack_fix_shadowarray,
tr("Fix Shadow Array Handles [EXPERIMENTAL]"),
tr("Reuse the last valid texture handle within a descriptor array if a slot is null. Fixes black shadows or missing textures in games that rely on array broadcasting (e.g., Cascaded Shadow Maps)."));
INSERT(Settings,
use_vulkan_driver_pipeline_cache,

35
src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp

@ -375,13 +375,37 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
add_image(desc, false);
}
}
bool fix_shadows = Settings::values.hack_fix_shadowarray.GetValue();
for (const auto& desc : info.texture_descriptors) {
u32 last_valid_first = 0;
u32 last_valid_second = 0;
for (u32 index = 0; index < desc.count; ++index) {
const auto handle{read_handle(desc, index)};
views[view_index++] = {handle.first};
auto handle = read_handle(desc, index);
if (fix_shadows) {
if (handle.first != 0) {
last_valid_first = handle.first;
last_valid_second = handle.second;
} else if (last_valid_first != 0) {
handle.first = last_valid_first;
handle.second = last_valid_second;
}
}
VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
samplers[sampler_index++] = sampler;
if (handle.first == 0) {
views[view_index++] = {
.index = 0,
.blacklist = false,
.id = {}
};
samplers[sampler_index++] = VideoCommon::NULL_SAMPLER_ID;
} else {
views[view_index++] = {handle.first};
VideoCommon::SamplerId sampler{
texture_cache.GetGraphicsSamplerId(handle.second)};
samplers[sampler_index++] = sampler;
}
}
}
if constexpr (Spec::has_images) {
@ -407,6 +431,9 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
if constexpr (Spec::enabled_stages[4]) {
config_stage(4);
}
// Data exists in the slot the shadows want but it seems this outputs a invalid image for all of them.
// The problem is either inside this function or the texture_descriptors above is pulling junk data
texture_cache.FillGraphicsImageViews<Spec::has_images>(std::span(views.data(), view_index));
VideoCommon::ImageViewInOut* texture_buffer_it{views.data()};

49
src/video_core/texture_cache/texture_cache.h

@ -52,8 +52,53 @@ TextureCache<P>::TextureCache(Runtime& runtime_, Tegra::MaxwellDeviceMemoryManag
// Make sure the first index is reserved for the null resources
// This way the null resource becomes a compile time constant
void(slot_images.insert(NullImageParams{}));
void(slot_image_views.insert(runtime, NullImageViewParams{}));
if( Settings::values.hack_fix_shadowarray.GetValue() ) {
ImageInfo null_image_info;
null_image_info.type = ImageType::e2D;
null_image_info.format = PixelFormat::A8B8G8R8_UNORM;
null_image_info.size = Extent3D{1, 1, 1};
null_image_info.resources.levels = 1;
null_image_info.resources.layers = 1;
null_image_info.num_samples = 1;
null_image_info.layer_stride = 0;
null_image_info.maybe_unaligned_layer_stride = 0;
null_image_info.num_samples = 1;
null_image_info.tile_width_spacing = 0;
const ImageId null_image_id = slot_images.insert(runtime, null_image_info, 0, 0);
// Upload white pixel (0xFFFFFFFF) to the image
Image& null_image = slot_images[null_image_id];
auto staging = runtime.UploadStagingBuffer(4); // 1 pixel = 4 bytes
std::memset(staging.mapped_span.data(), 0xFF, 4); // White pixel
BufferImageCopy copy{
.buffer_offset = 0,
.buffer_size = 4,
.buffer_row_length = 1,
.buffer_image_height = 1,
.image_subresource = {.base_level = 0, .base_layer = 0, .num_layers = 1},
.image_offset = {0, 0, 0},
.image_extent = {1, 1, 1},
};
null_image.UploadMemory(staging, std::array{copy});
runtime.InsertUploadMemoryBarrier();
// Create image view for the null texture
SubresourceRange null_range{
.base = {.level = 0, .layer = 0},
.extent = {.levels = 1, .layers = 1},
};
ImageViewInfo null_view_info(ImageViewType::e2D, PixelFormat::A8B8G8R8_UNORM, null_range);
void(slot_image_views.insert(runtime, null_view_info, null_image_id, null_image, slot_images));
}
else {
void(slot_images.insert(NullImageParams{}));
void(slot_image_views.insert(runtime, NullImageViewParams{}));
}
void(slot_samplers.insert(runtime, sampler_descriptor));
if constexpr (HAS_DEVICE_MEMORY_INFO) {

Loading…
Cancel
Save