mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-29 09:58:05 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f73164b191 | |||
| 35c420582f | |||
| b2b7a34957 | |||
| d7a564b088 | |||
| 1d57d4a73d | |||
| 3af03332cc | |||
| a6e506c328 |
+1
@@ -586,6 +586,7 @@ abstract class SettingsItem(
|
|||||||
IntSetting.FSR_SHARPENING_SLIDER,
|
IntSetting.FSR_SHARPENING_SLIDER,
|
||||||
titleId = R.string.fsr_sharpness,
|
titleId = R.string.fsr_sharpness,
|
||||||
descriptionId = R.string.fsr_sharpness_description,
|
descriptionId = R.string.fsr_sharpness_description,
|
||||||
|
max = 200,
|
||||||
units = "%"
|
units = "%"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1182,7 +1182,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
container,
|
container,
|
||||||
IntSetting.FSR_SHARPENING_SLIDER,
|
IntSetting.FSR_SHARPENING_SLIDER,
|
||||||
minValue = 0,
|
minValue = 0,
|
||||||
maxValue = 100,
|
maxValue = 200,
|
||||||
units = "%"
|
units = "%"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <bit>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
|
||||||
@@ -809,46 +810,46 @@ void BufferCache<P>::BindHostVertexBuffers() {
|
|||||||
|
|
||||||
if (use_optimized_vertex_buffers) {
|
if (use_optimized_vertex_buffers) {
|
||||||
auto& flags = maxwell3d->dirty.flags;
|
auto& flags = maxwell3d->dirty.flags;
|
||||||
u32 enabled_mask = enabled_vertex_buffers_mask;
|
const u32 enabled_mask = enabled_vertex_buffers_mask;
|
||||||
HostBindings<Buffer> bindings{};
|
bool any_dirty = false;
|
||||||
u32 last_index = (std::numeric_limits<u32>::max)();
|
u32 pending_mask = enabled_mask;
|
||||||
const auto flush_bindings = [&]() {
|
while (pending_mask != 0) {
|
||||||
if (bindings.buffers.empty()) {
|
const u32 index = std::countr_zero(pending_mask);
|
||||||
return;
|
pending_mask &= (pending_mask - 1);
|
||||||
}
|
|
||||||
bindings.max_index = bindings.min_index + static_cast<u32>(bindings.buffers.size());
|
|
||||||
runtime.BindVertexBuffers(bindings);
|
|
||||||
bindings = HostBindings<Buffer>{};
|
|
||||||
last_index = (std::numeric_limits<u32>::max)();
|
|
||||||
};
|
|
||||||
while (enabled_mask != 0) {
|
|
||||||
const u32 index = std::countr_zero(enabled_mask);
|
|
||||||
enabled_mask &= (enabled_mask - 1);
|
|
||||||
const Binding& binding = VertexBufferSlot(index);
|
const Binding& binding = VertexBufferSlot(index);
|
||||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||||
TouchBuffer(buffer, binding.buffer_id);
|
TouchBuffer(buffer, binding.buffer_id);
|
||||||
SynchronizeBuffer(buffer, binding.device_addr, binding.size);
|
SynchronizeBuffer(buffer, binding.device_addr, binding.size);
|
||||||
if (!flags[Dirty::VertexBuffer0 + index]) {
|
any_dirty |= flags[Dirty::VertexBuffer0 + index];
|
||||||
flush_bindings();
|
}
|
||||||
continue;
|
if (enabled_mask == 0 || !any_dirty) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
const u32 min_index = static_cast<u32>(std::countr_zero(enabled_mask));
|
||||||
|
const u32 max_index = 32u - static_cast<u32>(std::countl_zero(enabled_mask));
|
||||||
|
HostBindings<Buffer> bindings{};
|
||||||
|
bindings.min_index = min_index;
|
||||||
|
bindings.max_index = max_index;
|
||||||
|
for (u32 index = min_index; index < max_index; ++index) {
|
||||||
flags[Dirty::VertexBuffer0 + index] = false;
|
flags[Dirty::VertexBuffer0 + index] = false;
|
||||||
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
|
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
|
||||||
|
if ((enabled_mask & (1u << index)) == 0) {
|
||||||
|
bindings.buffers.push_back(&slot_buffers[NULL_BUFFER_ID]);
|
||||||
|
bindings.offsets.push_back(0);
|
||||||
|
bindings.sizes.push_back(0);
|
||||||
|
bindings.strides.push_back(stride);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const Binding& binding = VertexBufferSlot(index);
|
||||||
|
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||||
const u32 offset = buffer.Offset(binding.device_addr);
|
const u32 offset = buffer.Offset(binding.device_addr);
|
||||||
buffer.MarkUsage(offset, binding.size);
|
buffer.MarkUsage(offset, binding.size);
|
||||||
if (!bindings.buffers.empty() && index != last_index + 1) {
|
|
||||||
flush_bindings();
|
|
||||||
}
|
|
||||||
if (bindings.buffers.empty()) {
|
|
||||||
bindings.min_index = index;
|
|
||||||
}
|
|
||||||
bindings.buffers.push_back(&buffer);
|
bindings.buffers.push_back(&buffer);
|
||||||
bindings.offsets.push_back(offset);
|
bindings.offsets.push_back(offset);
|
||||||
bindings.sizes.push_back(binding.size);
|
bindings.sizes.push_back(binding.size);
|
||||||
bindings.strides.push_back(stride);
|
bindings.strides.push_back(stride);
|
||||||
last_index = index;
|
|
||||||
}
|
}
|
||||||
flush_bindings();
|
runtime.BindVertexBuffers(bindings);
|
||||||
} else {
|
} else {
|
||||||
HostBindings<typename P::Buffer> host_bindings;
|
HostBindings<typename P::Buffer> host_bindings;
|
||||||
bool any_valid{false};
|
bool any_valid{false};
|
||||||
|
|||||||
@@ -17,11 +17,13 @@ set(SHADER_FILES
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp
|
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
|
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d_buffer.comp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp
|
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp
|
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_buffer.comp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#version 430
|
||||||
|
|
||||||
|
#extension GL_EXT_shader_16bit_storage : require
|
||||||
|
#extension GL_EXT_shader_8bit_storage : require
|
||||||
|
|
||||||
|
#define BINDING_INPUT_BUFFER 0
|
||||||
|
#define BINDING_OUTPUT_BUFFER 1
|
||||||
|
|
||||||
|
layout(push_constant) uniform PushConstants {
|
||||||
|
uvec3 dim;
|
||||||
|
uint bytes_per_block_log2;
|
||||||
|
|
||||||
|
uvec3 origin;
|
||||||
|
uint layer_stride;
|
||||||
|
|
||||||
|
uint block_size;
|
||||||
|
uint x_shift;
|
||||||
|
uint block_height;
|
||||||
|
uint block_height_mask;
|
||||||
|
} pc;
|
||||||
|
|
||||||
|
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
|
||||||
|
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
|
||||||
|
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
|
||||||
|
|
||||||
|
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
|
||||||
|
uint out_u32[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(local_size_x = 16, local_size_y = 8, local_size_z = 1) in;
|
||||||
|
|
||||||
|
const uint GOB_SIZE_X = 64;
|
||||||
|
const uint GOB_SIZE_Y = 8;
|
||||||
|
|
||||||
|
const uint GOB_SIZE_X_SHIFT = 6;
|
||||||
|
const uint GOB_SIZE_Y_SHIFT = 3;
|
||||||
|
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
|
||||||
|
|
||||||
|
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
|
||||||
|
|
||||||
|
uint SwizzleTable(uint pos) {
|
||||||
|
const uint t[8] = uint[](
|
||||||
|
0x12100200, 0x13110301, 0x16140604, 0x17150705,
|
||||||
|
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
|
||||||
|
);
|
||||||
|
const uint i = pos >> 4;
|
||||||
|
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
|
||||||
|
return (h << 4) | (pos & 0xf);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint SwizzleOffset(uvec2 pos) {
|
||||||
|
pos = pos & SWIZZLE_MASK;
|
||||||
|
return SwizzleTable(pos.y * 64u + pos.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
uvec4 ReadTexel(uint offset) {
|
||||||
|
switch (pc.bytes_per_block_log2) {
|
||||||
|
case 2u:
|
||||||
|
return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
|
||||||
|
case 3u:
|
||||||
|
return uvec4(u64data[offset / 8u], 0u, 0u);
|
||||||
|
case 4u:
|
||||||
|
return u128data[offset / 16u];
|
||||||
|
}
|
||||||
|
return uvec4(0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
uvec3 coord = gl_GlobalInvocationID;
|
||||||
|
if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uvec3 pos = coord + pc.origin;
|
||||||
|
pos.x <<= pc.bytes_per_block_log2;
|
||||||
|
|
||||||
|
uint swizzle = SwizzleOffset(pos.xy);
|
||||||
|
uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
|
||||||
|
|
||||||
|
uint offset = 0u;
|
||||||
|
offset += pos.z * pc.layer_stride;
|
||||||
|
offset += (block_y >> pc.block_height) * pc.block_size;
|
||||||
|
offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
|
||||||
|
offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
|
||||||
|
offset += swizzle;
|
||||||
|
|
||||||
|
uvec4 texel = ReadTexel(offset);
|
||||||
|
|
||||||
|
uint words = 1u << (pc.bytes_per_block_log2 - 2u);
|
||||||
|
uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
|
||||||
|
uint out_idx = linear_index * words;
|
||||||
|
|
||||||
|
out_u32[out_idx] = texel.x;
|
||||||
|
if (words > 1u) {
|
||||||
|
out_u32[out_idx + 1u] = texel.y;
|
||||||
|
}
|
||||||
|
if (words > 2u) {
|
||||||
|
out_u32[out_idx + 2u] = texel.z;
|
||||||
|
out_u32[out_idx + 3u] = texel.w;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#version 430
|
||||||
|
|
||||||
|
#define BINDING_INPUT_BUFFER 0
|
||||||
|
#define BINDING_OUTPUT_BUFFER 1
|
||||||
|
|
||||||
|
layout(push_constant) uniform PushConstants {
|
||||||
|
uvec3 dim;
|
||||||
|
uint bytes_per_block_log2;
|
||||||
|
|
||||||
|
uvec3 origin;
|
||||||
|
uint slice_size;
|
||||||
|
|
||||||
|
uint block_size;
|
||||||
|
uint x_shift;
|
||||||
|
uint block_height;
|
||||||
|
uint block_height_mask;
|
||||||
|
|
||||||
|
uint block_depth;
|
||||||
|
uint block_depth_mask;
|
||||||
|
} pc;
|
||||||
|
|
||||||
|
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
|
||||||
|
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
|
||||||
|
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
|
||||||
|
|
||||||
|
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
|
||||||
|
uint out_u32[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in;
|
||||||
|
|
||||||
|
const uint GOB_SIZE_X = 64;
|
||||||
|
const uint GOB_SIZE_Y = 8;
|
||||||
|
|
||||||
|
const uint GOB_SIZE_X_SHIFT = 6;
|
||||||
|
const uint GOB_SIZE_Y_SHIFT = 3;
|
||||||
|
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
|
||||||
|
|
||||||
|
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
|
||||||
|
|
||||||
|
uint SwizzleTable(uint pos) {
|
||||||
|
const uint t[8] = uint[](
|
||||||
|
0x12100200, 0x13110301, 0x16140604, 0x17150705,
|
||||||
|
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
|
||||||
|
);
|
||||||
|
const uint i = pos >> 4;
|
||||||
|
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
|
||||||
|
return (h << 4) | (pos & 0xf);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint SwizzleOffset(uvec2 pos) {
|
||||||
|
pos = pos & SWIZZLE_MASK;
|
||||||
|
return SwizzleTable(pos.y * 64u + pos.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
uvec4 ReadTexel(uint offset) {
|
||||||
|
switch (pc.bytes_per_block_log2) {
|
||||||
|
case 2u:
|
||||||
|
return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
|
||||||
|
case 3u:
|
||||||
|
return uvec4(u64data[offset / 8u], 0u, 0u);
|
||||||
|
case 4u:
|
||||||
|
return u128data[offset / 16u];
|
||||||
|
}
|
||||||
|
return uvec4(0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
uvec3 coord = gl_GlobalInvocationID;
|
||||||
|
if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uvec3 pos = coord + pc.origin;
|
||||||
|
pos.x <<= pc.bytes_per_block_log2;
|
||||||
|
|
||||||
|
uint swizzle = SwizzleOffset(pos.xy);
|
||||||
|
uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
|
||||||
|
|
||||||
|
uint offset = 0u;
|
||||||
|
offset += (pos.z >> pc.block_depth) * pc.slice_size;
|
||||||
|
offset += (pos.z & pc.block_depth_mask) << (GOB_SIZE_SHIFT + pc.block_height);
|
||||||
|
offset += (block_y >> pc.block_height) * pc.block_size;
|
||||||
|
offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
|
||||||
|
offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
|
||||||
|
offset += swizzle;
|
||||||
|
|
||||||
|
uvec4 texel = ReadTexel(offset);
|
||||||
|
|
||||||
|
uint words = 1u << (pc.bytes_per_block_log2 - 2u);
|
||||||
|
uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
|
||||||
|
uint out_idx = linear_index * words;
|
||||||
|
|
||||||
|
out_u32[out_idx] = texel.x;
|
||||||
|
if (words > 1u) {
|
||||||
|
out_u32[out_idx + 1u] = texel.y;
|
||||||
|
}
|
||||||
|
if (words > 2u) {
|
||||||
|
out_u32[out_idx + 2u] = texel.z;
|
||||||
|
out_u32[out_idx + 3u] = texel.w;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ layout(push_constant) uniform constants {
|
|||||||
vec2 scale;
|
vec2 scale;
|
||||||
vec2 size;
|
vec2 size;
|
||||||
vec2 resize_factor;
|
vec2 resize_factor;
|
||||||
|
vec2 crop_offset;
|
||||||
float edge_sharpness;
|
float edge_sharpness;
|
||||||
};
|
};
|
||||||
layout(location = 0) out highp vec2 texcoord;
|
layout(location = 0) out highp vec2 texcoord;
|
||||||
@@ -15,5 +16,5 @@ void main() {
|
|||||||
float x = float((gl_VertexIndex & 1) << 2);
|
float x = float((gl_VertexIndex & 1) << 2);
|
||||||
float y = float((gl_VertexIndex & 2) << 1);
|
float y = float((gl_VertexIndex & 2) << 1);
|
||||||
gl_Position = vec4(x - 1.0f, y - 1.0f, 0.0, 1.0f) * vec4(sign(resize_factor), 1.f, 1.f);
|
gl_Position = vec4(x - 1.0f, y - 1.0f, 0.0, 1.0f) * vec4(sign(resize_factor), 1.f, 1.f);
|
||||||
texcoord = vec2(x, y) * abs(resize_factor) * 0.5;
|
texcoord = crop_offset + vec2(x, y) * abs(resize_factor) * 0.5;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ layout(push_constant) uniform constants {
|
|||||||
vec2 scale;
|
vec2 scale;
|
||||||
vec2 size;
|
vec2 size;
|
||||||
vec2 resize_factor;
|
vec2 resize_factor;
|
||||||
|
vec2 crop_offset;
|
||||||
float edge_sharpness;
|
float edge_sharpness;
|
||||||
};
|
};
|
||||||
layout(set = 0, binding = 0) uniform sampler2D sampler0;
|
layout(set = 0, binding = 0) uniform sampler2D sampler0;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
layout( push_constant ) uniform constants {
|
layout( push_constant ) uniform constants {
|
||||||
vec4 ViewportInfo[1];
|
vec4 ViewportInfo[1];
|
||||||
vec2 ResizeFactor;
|
vec2 ResizeFactor;
|
||||||
|
vec2 CropOffset;
|
||||||
float EdgeSharpness;
|
float EdgeSharpness;
|
||||||
};
|
};
|
||||||
layout(set = 0, binding = 0) uniform sampler2D ps0;
|
layout(set = 0, binding = 0) uniform sampler2D ps0;
|
||||||
|
|||||||
@@ -1191,7 +1191,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
|
|||||||
.subpass = 0,
|
.subpass = 0,
|
||||||
.basePipelineHandle = VK_NULL_HANDLE,
|
.basePipelineHandle = VK_NULL_HANDLE,
|
||||||
.basePipelineIndex = 0,
|
.basePipelineIndex = 0,
|
||||||
}));
|
}, device.StaticPipelineCache()));
|
||||||
return *blit_color_pipelines.back();
|
return *blit_color_pipelines.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1223,7 +1223,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
|
|||||||
.subpass = 0,
|
.subpass = 0,
|
||||||
.basePipelineHandle = VK_NULL_HANDLE,
|
.basePipelineHandle = VK_NULL_HANDLE,
|
||||||
.basePipelineIndex = 0,
|
.basePipelineIndex = 0,
|
||||||
}));
|
}, device.StaticPipelineCache()));
|
||||||
return *blit_depth_stencil_pipelines.back();
|
return *blit_depth_stencil_pipelines.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1276,7 +1276,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
|
|||||||
.subpass = 0,
|
.subpass = 0,
|
||||||
.basePipelineHandle = VK_NULL_HANDLE,
|
.basePipelineHandle = VK_NULL_HANDLE,
|
||||||
.basePipelineIndex = 0,
|
.basePipelineIndex = 0,
|
||||||
}));
|
}, device.StaticPipelineCache()));
|
||||||
return *clear_color_pipelines.back();
|
return *clear_color_pipelines.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1332,7 +1332,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
|
|||||||
.subpass = 0,
|
.subpass = 0,
|
||||||
.basePipelineHandle = VK_NULL_HANDLE,
|
.basePipelineHandle = VK_NULL_HANDLE,
|
||||||
.basePipelineIndex = 0,
|
.basePipelineIndex = 0,
|
||||||
}));
|
}, device.StaticPipelineCache()));
|
||||||
return *clear_stencil_pipelines.back();
|
return *clear_stencil_pipelines.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1375,7 +1375,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip
|
|||||||
.subpass = 0,
|
.subpass = 0,
|
||||||
.basePipelineHandle = VK_NULL_HANDLE,
|
.basePipelineHandle = VK_NULL_HANDLE,
|
||||||
.basePipelineIndex = 0,
|
.basePipelineIndex = 0,
|
||||||
}));
|
}, device.StaticPipelineCache()));
|
||||||
return *blit_msaa_color_pipelines.back();
|
return *blit_msaa_color_pipelines.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1413,7 +1413,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPas
|
|||||||
.subpass = 0,
|
.subpass = 0,
|
||||||
.basePipelineHandle = VK_NULL_HANDLE,
|
.basePipelineHandle = VK_NULL_HANDLE,
|
||||||
.basePipelineIndex = 0,
|
.basePipelineIndex = 0,
|
||||||
}));
|
}, device.StaticPipelineCache()));
|
||||||
return *pipelines.back();
|
return *pipelines.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1458,7 +1458,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline
|
|||||||
.subpass = 0,
|
.subpass = 0,
|
||||||
.basePipelineHandle = VK_NULL_HANDLE,
|
.basePipelineHandle = VK_NULL_HANDLE,
|
||||||
.basePipelineIndex = 0,
|
.basePipelineIndex = 0,
|
||||||
}));
|
}, device.StaticPipelineCache()));
|
||||||
return *msaa_copy_pipelines.back();
|
return *msaa_copy_pipelines.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1499,7 +1499,7 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
|
|||||||
.subpass = 0,
|
.subpass = 0,
|
||||||
.basePipelineHandle = VK_NULL_HANDLE,
|
.basePipelineHandle = VK_NULL_HANDLE,
|
||||||
.basePipelineIndex = 0,
|
.basePipelineIndex = 0,
|
||||||
});
|
}, device.StaticPipelineCache());
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
|
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
|
||||||
@@ -1542,7 +1542,7 @@ void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass rende
|
|||||||
.subpass = 0,
|
.subpass = 0,
|
||||||
.basePipelineHandle = VK_NULL_HANDLE,
|
.basePipelineHandle = VK_NULL_HANDLE,
|
||||||
.basePipelineIndex = 0,
|
.basePipelineIndex = 0,
|
||||||
});
|
}, device.StaticPipelineCache());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 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
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/div_ceil.h"
|
#include "common/div_ceil.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
@@ -17,7 +19,7 @@
|
|||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
using PushConstants = std::array<u32, 4 + 2 + 1>;
|
using PushConstants = std::array<u32, 4 + 2 + 2 + 1>;
|
||||||
|
|
||||||
SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent, bool edge_dir)
|
SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent, bool edge_dir)
|
||||||
: m_memory_allocator{memory_allocator}
|
: m_memory_allocator{memory_allocator}
|
||||||
@@ -100,26 +102,28 @@ VkImageView SGSR::Draw(const Device& device, Scheduler& scheduler, size_t image_
|
|||||||
|
|
||||||
const f32 input_image_width = f32(input_image_extent.width);
|
const f32 input_image_width = f32(input_image_extent.width);
|
||||||
const f32 input_image_height = f32(input_image_extent.height);
|
const f32 input_image_height = f32(input_image_extent.height);
|
||||||
const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width;
|
const f32 crop_width = (crop_rect.right - crop_rect.left) * input_image_width;
|
||||||
const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
|
const f32 crop_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
|
||||||
// expected [0, 2]
|
static constexpr f32 EDGE_SHARPNESS_MAX = 2.0f;
|
||||||
const f32 sharpening = f32(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
|
const f32 edge_sharpness =
|
||||||
|
EDGE_SHARPNESS_MAX - f32(Settings::values.fsr_sharpening_slider.GetValue()) / 200.0f;
|
||||||
|
|
||||||
// p = (tex * viewport) / input = [0,n] (normalized texcoords)
|
|
||||||
// p * input = [0,1024], [0,768]
|
|
||||||
// layout( push_constant ) uniform constants {
|
// layout( push_constant ) uniform constants {
|
||||||
// highp vec4 ViewportInfo[1];
|
// highp vec4 ViewportInfo[1];
|
||||||
// highp vec2 ResizeFactor;
|
// highp vec2 ResizeFactor;
|
||||||
|
// highp vec2 CropOffset;
|
||||||
// highp float EdgeSharpness;
|
// highp float EdgeSharpness;
|
||||||
// };
|
// };
|
||||||
PushConstants viewport_con{};
|
PushConstants viewport_con{};
|
||||||
viewport_con[0] = std::bit_cast<u32>(std::abs(1.f / viewport_width));
|
viewport_con[0] = std::bit_cast<u32>(1.f / input_image_width);
|
||||||
viewport_con[1] = std::bit_cast<u32>(std::abs(1.f / viewport_height));
|
viewport_con[1] = std::bit_cast<u32>(1.f / input_image_height);
|
||||||
viewport_con[2] = std::bit_cast<u32>(std::abs(viewport_width));
|
viewport_con[2] = std::bit_cast<u32>(input_image_width);
|
||||||
viewport_con[3] = std::bit_cast<u32>(std::abs(viewport_height));
|
viewport_con[3] = std::bit_cast<u32>(input_image_height);
|
||||||
viewport_con[4] = std::bit_cast<u32>(viewport_width / input_image_width);
|
viewport_con[4] = std::bit_cast<u32>(crop_width / input_image_width);
|
||||||
viewport_con[5] = std::bit_cast<u32>(viewport_height / input_image_height);
|
viewport_con[5] = std::bit_cast<u32>(crop_height / input_image_height);
|
||||||
viewport_con[6] = std::bit_cast<u32>(sharpening);
|
viewport_con[6] = std::bit_cast<u32>((std::min)(crop_rect.left, crop_rect.right));
|
||||||
|
viewport_con[7] = std::bit_cast<u32>((std::min)(crop_rect.top, crop_rect.bottom));
|
||||||
|
viewport_con[8] = std::bit_cast<u32>(edge_sharpness);
|
||||||
|
|
||||||
UploadImages(device, scheduler);
|
UploadImages(device, scheduler);
|
||||||
UpdateDescriptorSets(device, source_image_view, image_index);
|
UpdateDescriptorSets(device, source_image_view, image_index);
|
||||||
|
|||||||
@@ -491,7 +491,7 @@ static vk::Pipeline CreateWrappedPipelineImpl(
|
|||||||
.subpass = 0,
|
.subpass = 0,
|
||||||
.basePipelineHandle = 0,
|
.basePipelineHandle = 0,
|
||||||
.basePipelineIndex = 0,
|
.basePipelineIndex = 0,
|
||||||
});
|
}, device.StaticPipelineCache());
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
|
vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
|
||||||
|
|||||||
@@ -22,7 +22,9 @@
|
|||||||
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
|
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
|
||||||
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
|
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
|
||||||
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
|
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
|
||||||
|
#include "video_core/host_shaders/block_linear_unswizzle_2d_buffer_comp_spv.h"
|
||||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
|
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
|
||||||
|
#include "video_core/host_shaders/block_linear_unswizzle_3d_buffer_comp_spv.h"
|
||||||
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
||||||
#include "video_core/surface.h"
|
#include "video_core/surface.h"
|
||||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||||
@@ -268,7 +270,7 @@ ComputePass::ComputePass(const Device& device_, Scheduler& scheduler, Descriptor
|
|||||||
.layout = *layout,
|
.layout = *layout,
|
||||||
.basePipelineHandle = {},
|
.basePipelineHandle = {},
|
||||||
.basePipelineIndex = 0,
|
.basePipelineIndex = 0,
|
||||||
});
|
}, device.StaticPipelineCache());
|
||||||
}
|
}
|
||||||
|
|
||||||
ComputePass::~ComputePass() = default;
|
ComputePass::~ComputePass() = default;
|
||||||
@@ -872,4 +874,479 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr u32 BL2D_BINDING_INPUT_BUFFER = 0;
|
||||||
|
constexpr u32 BL2D_BINDING_OUTPUT_BUFFER = 1;
|
||||||
|
|
||||||
|
struct alignas(16) BlockLinearUnswizzle2DPushConstants {
|
||||||
|
std::array<u32, 3> dim;
|
||||||
|
u32 bytes_per_block_log2;
|
||||||
|
std::array<u32, 3> origin;
|
||||||
|
u32 layer_stride;
|
||||||
|
u32 block_size;
|
||||||
|
u32 x_shift;
|
||||||
|
u32 block_height;
|
||||||
|
u32 block_height_mask;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(BlockLinearUnswizzle2DPushConstants) <= 128);
|
||||||
|
|
||||||
|
constexpr std::array<VkDescriptorSetLayoutBinding, 2> BL2D_BINDINGS{{
|
||||||
|
{
|
||||||
|
.binding = BL2D_BINDING_INPUT_BUFFER,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||||
|
.pImmutableSamplers = nullptr,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.binding = BL2D_BINDING_OUTPUT_BUFFER,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||||
|
.pImmutableSamplers = nullptr,
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
constexpr std::array<VkDescriptorUpdateTemplateEntry, 2> BL2D_TEMPLATE{{
|
||||||
|
{
|
||||||
|
.dstBinding = BL2D_BINDING_INPUT_BUFFER,
|
||||||
|
.dstArrayElement = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||||
|
.offset = BL2D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||||
|
.stride = sizeof(DescriptorUpdateEntry),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.dstBinding = BL2D_BINDING_OUTPUT_BUFFER,
|
||||||
|
.dstArrayElement = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||||
|
.offset = BL2D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||||
|
.stride = sizeof(DescriptorUpdateEntry),
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
constexpr DescriptorBankInfo BL2D_BANK_INFO{
|
||||||
|
.uniform_buffers = 0,
|
||||||
|
.storage_buffers = 2,
|
||||||
|
.texture_buffers = 0,
|
||||||
|
.image_buffers = 0,
|
||||||
|
.textures = 0,
|
||||||
|
.images = 0,
|
||||||
|
.score = 2,
|
||||||
|
};
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass(
|
||||||
|
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||||
|
StagingBufferPool& staging_buffer_pool_,
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||||
|
: ComputePass(device_, scheduler_, descriptor_pool_, BL2D_BINDINGS, BL2D_TEMPLATE,
|
||||||
|
BL2D_BANK_INFO,
|
||||||
|
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle2DPushConstants)>,
|
||||||
|
BLOCK_LINEAR_UNSWIZZLE_2D_BUFFER_COMP_SPV),
|
||||||
|
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
|
||||||
|
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||||
|
|
||||||
|
BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default;
|
||||||
|
|
||||||
|
bool BlockLinearUnswizzle2DPass::IsSupported(const VideoCommon::ImageInfo& info) {
|
||||||
|
if (info.type != VideoCommon::ImageType::e2D) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (info.resources.levels != 1 || info.resources.layers != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (info.num_samples > 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (VideoCore::Surface::IsPixelFormatASTC(info.format) ||
|
||||||
|
VideoCore::Surface::IsPixelFormatBCn(info.format)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
|
||||||
|
if (bytes_per_block != 4 && bytes_per_block != 8 && bytes_per_block != 16) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return VideoCore::Surface::DefaultBlockWidth(info.format) == 1 &&
|
||||||
|
VideoCore::Surface::DefaultBlockHeight(info.format) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockLinearUnswizzle2DPass::Unswizzle(
|
||||||
|
Image& image, const StagingBufferRef& swizzled,
|
||||||
|
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||||
|
if (swizzles.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const VideoCommon::SwizzleParameters& sw = swizzles.front();
|
||||||
|
const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams(sw, image.info);
|
||||||
|
|
||||||
|
const u32 width = sw.num_tiles.width;
|
||||||
|
const u32 height = sw.num_tiles.height;
|
||||||
|
const u32 depth = image.info.resources.layers;
|
||||||
|
const u32 bytes_per_block = 1u << params.bytes_per_block_log2;
|
||||||
|
const VkDeviceSize output_size =
|
||||||
|
static_cast<VkDeviceSize>(width) * height * depth * bytes_per_block;
|
||||||
|
|
||||||
|
const StagingBufferRef output =
|
||||||
|
staging_buffer_pool.Request(static_cast<size_t>(output_size), MemoryUsage::DeviceLocal);
|
||||||
|
|
||||||
|
BlockLinearUnswizzle2DPushConstants pc{};
|
||||||
|
pc.dim = {width, height, depth};
|
||||||
|
pc.bytes_per_block_log2 = params.bytes_per_block_log2;
|
||||||
|
pc.origin = params.origin;
|
||||||
|
pc.layer_stride = params.layer_stride;
|
||||||
|
pc.block_size = params.block_size;
|
||||||
|
pc.x_shift = params.x_shift;
|
||||||
|
pc.block_height = params.block_height;
|
||||||
|
pc.block_height_mask = params.block_height_mask;
|
||||||
|
|
||||||
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
|
||||||
|
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||||
|
compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset,
|
||||||
|
image.guest_size_bytes - sw.buffer_offset);
|
||||||
|
compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size);
|
||||||
|
|
||||||
|
const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
|
||||||
|
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||||
|
|
||||||
|
const u32 gx = Common::DivCeil(width, 16u);
|
||||||
|
const u32 gy = Common::DivCeil(height, 8u);
|
||||||
|
const bool is_initialized = image.ExchangeInitialization();
|
||||||
|
|
||||||
|
const VkBuffer out_buffer = output.buffer;
|
||||||
|
const VkDeviceSize out_offset = output.offset;
|
||||||
|
const VkImage dst_image = image.Handle();
|
||||||
|
const VkImageAspectFlags aspect = image.AspectMask();
|
||||||
|
|
||||||
|
scheduler.Record([this, set, descriptor_data, pc, gx, gy, depth, output_size, out_buffer,
|
||||||
|
out_offset, dst_image, aspect, width, height,
|
||||||
|
is_initialized](vk::CommandBuffer cmdbuf) {
|
||||||
|
if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||||
|
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
|
||||||
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||||
|
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc);
|
||||||
|
cmdbuf.Dispatch(gx, gy, depth);
|
||||||
|
|
||||||
|
const VkBufferMemoryBarrier buffer_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.buffer = out_buffer,
|
||||||
|
.offset = out_offset,
|
||||||
|
.size = output_size,
|
||||||
|
};
|
||||||
|
const VkImageMemoryBarrier pre_copy{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_READ_BIT
|
||||||
|
: VK_ACCESS_NONE),
|
||||||
|
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = dst_image,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = aspect,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
||||||
|
(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE
|
||||||
|
: VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy);
|
||||||
|
|
||||||
|
const VkBufferImageCopy copy{
|
||||||
|
.bufferOffset = out_offset,
|
||||||
|
.bufferRowLength = 0,
|
||||||
|
.bufferImageHeight = 0,
|
||||||
|
.imageSubresource{
|
||||||
|
.aspectMask = aspect,
|
||||||
|
.mipLevel = 0,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = depth,
|
||||||
|
},
|
||||||
|
.imageOffset = {0, 0, 0},
|
||||||
|
.imageExtent = {width, height, 1},
|
||||||
|
};
|
||||||
|
cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
|
||||||
|
|
||||||
|
const VkImageMemoryBarrier post_copy{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = dst_image,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = aspect,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
|
||||||
|
0, {}, {}, post_copy);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr u32 BL3DB_BINDING_INPUT_BUFFER = 0;
|
||||||
|
constexpr u32 BL3DB_BINDING_OUTPUT_BUFFER = 1;
|
||||||
|
|
||||||
|
struct alignas(16) BlockLinearUnswizzle3DBufferPushConstants {
|
||||||
|
std::array<u32, 3> dim;
|
||||||
|
u32 bytes_per_block_log2;
|
||||||
|
std::array<u32, 3> origin;
|
||||||
|
u32 slice_size;
|
||||||
|
u32 block_size;
|
||||||
|
u32 x_shift;
|
||||||
|
u32 block_height;
|
||||||
|
u32 block_height_mask;
|
||||||
|
u32 block_depth;
|
||||||
|
u32 block_depth_mask;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(BlockLinearUnswizzle3DBufferPushConstants) <= 128);
|
||||||
|
|
||||||
|
constexpr std::array<VkDescriptorSetLayoutBinding, 2> BL3DB_BINDINGS{{
|
||||||
|
{
|
||||||
|
.binding = BL3DB_BINDING_INPUT_BUFFER,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||||
|
.pImmutableSamplers = nullptr,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.binding = BL3DB_BINDING_OUTPUT_BUFFER,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||||
|
.pImmutableSamplers = nullptr,
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
constexpr std::array<VkDescriptorUpdateTemplateEntry, 2> BL3DB_TEMPLATE{{
|
||||||
|
{
|
||||||
|
.dstBinding = BL3DB_BINDING_INPUT_BUFFER,
|
||||||
|
.dstArrayElement = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||||
|
.offset = BL3DB_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||||
|
.stride = sizeof(DescriptorUpdateEntry),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.dstBinding = BL3DB_BINDING_OUTPUT_BUFFER,
|
||||||
|
.dstArrayElement = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||||
|
.offset = BL3DB_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||||
|
.stride = sizeof(DescriptorUpdateEntry),
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
constexpr DescriptorBankInfo BL3DB_BANK_INFO{
|
||||||
|
.uniform_buffers = 0,
|
||||||
|
.storage_buffers = 2,
|
||||||
|
.texture_buffers = 0,
|
||||||
|
.image_buffers = 0,
|
||||||
|
.textures = 0,
|
||||||
|
.images = 0,
|
||||||
|
.score = 2,
|
||||||
|
};
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
BlockLinearUnswizzle3DBufferPass::BlockLinearUnswizzle3DBufferPass(
|
||||||
|
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||||
|
StagingBufferPool& staging_buffer_pool_,
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||||
|
: ComputePass(device_, scheduler_, descriptor_pool_, BL3DB_BINDINGS, BL3DB_TEMPLATE,
|
||||||
|
BL3DB_BANK_INFO,
|
||||||
|
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle3DBufferPushConstants)>,
|
||||||
|
BLOCK_LINEAR_UNSWIZZLE_3D_BUFFER_COMP_SPV),
|
||||||
|
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
|
||||||
|
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||||
|
|
||||||
|
BlockLinearUnswizzle3DBufferPass::~BlockLinearUnswizzle3DBufferPass() = default;
|
||||||
|
|
||||||
|
bool BlockLinearUnswizzle3DBufferPass::IsSupported(const Device& device,
|
||||||
|
const VideoCommon::ImageInfo& info) {
|
||||||
|
if (info.type != VideoCommon::ImageType::e3D) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (info.resources.levels != 1 || info.resources.layers != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (info.num_samples > 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (info.size.depth <= 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (VideoCore::Surface::IsPixelFormatASTC(info.format)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (VideoCore::Surface::IsPixelFormatBCn(info.format) && !device.IsOptimalBcnSupported()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
|
||||||
|
return bytes_per_block == 4 || bytes_per_block == 8 || bytes_per_block == 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockLinearUnswizzle3DBufferPass::Unswizzle(
|
||||||
|
Image& image, const StagingBufferRef& swizzled,
|
||||||
|
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||||
|
if (swizzles.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VideoCommon::SwizzleParameters& sw = swizzles.front();
|
||||||
|
const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams(sw, image.info);
|
||||||
|
|
||||||
|
const u32 blocks_x = sw.num_tiles.width;
|
||||||
|
const u32 blocks_y = sw.num_tiles.height;
|
||||||
|
const u32 blocks_z = sw.num_tiles.depth;
|
||||||
|
const u32 bytes_per_block = 1u << params.bytes_per_block_log2;
|
||||||
|
const VkDeviceSize output_size =
|
||||||
|
static_cast<VkDeviceSize>(blocks_x) * blocks_y * blocks_z * bytes_per_block;
|
||||||
|
|
||||||
|
const StagingBufferRef output =
|
||||||
|
staging_buffer_pool.Request(static_cast<size_t>(output_size), MemoryUsage::DeviceLocal);
|
||||||
|
|
||||||
|
BlockLinearUnswizzle3DBufferPushConstants pc{};
|
||||||
|
pc.dim = {blocks_x, blocks_y, blocks_z};
|
||||||
|
pc.bytes_per_block_log2 = params.bytes_per_block_log2;
|
||||||
|
pc.origin = params.origin;
|
||||||
|
pc.slice_size = params.slice_size;
|
||||||
|
pc.block_size = params.block_size;
|
||||||
|
pc.x_shift = params.x_shift;
|
||||||
|
pc.block_height = params.block_height;
|
||||||
|
pc.block_height_mask = params.block_height_mask;
|
||||||
|
pc.block_depth = params.block_depth;
|
||||||
|
pc.block_depth_mask = params.block_depth_mask;
|
||||||
|
|
||||||
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
|
||||||
|
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||||
|
compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset,
|
||||||
|
image.guest_size_bytes - sw.buffer_offset);
|
||||||
|
compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size);
|
||||||
|
|
||||||
|
const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
|
||||||
|
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||||
|
|
||||||
|
const u32 gx = Common::DivCeil(blocks_x, 8u);
|
||||||
|
const u32 gy = Common::DivCeil(blocks_y, 8u);
|
||||||
|
const u32 gz = Common::DivCeil(blocks_z, 4u);
|
||||||
|
const bool is_initialized = image.ExchangeInitialization();
|
||||||
|
|
||||||
|
const VkBuffer out_buffer = output.buffer;
|
||||||
|
const VkDeviceSize out_offset = output.offset;
|
||||||
|
const VkImage dst_image = image.Handle();
|
||||||
|
const VkImageAspectFlags aspect = image.AspectMask();
|
||||||
|
const VkExtent3D extent{
|
||||||
|
.width = image.info.size.width,
|
||||||
|
.height = image.info.size.height,
|
||||||
|
.depth = image.info.size.depth,
|
||||||
|
};
|
||||||
|
|
||||||
|
scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, output_size, out_buffer,
|
||||||
|
out_offset, dst_image, aspect, extent,
|
||||||
|
is_initialized](vk::CommandBuffer cmdbuf) {
|
||||||
|
if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||||
|
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
|
||||||
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||||
|
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc);
|
||||||
|
cmdbuf.Dispatch(gx, gy, gz);
|
||||||
|
|
||||||
|
const VkBufferMemoryBarrier buffer_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.buffer = out_buffer,
|
||||||
|
.offset = out_offset,
|
||||||
|
.size = output_size,
|
||||||
|
};
|
||||||
|
const VkImageMemoryBarrier pre_copy{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_READ_BIT
|
||||||
|
: VK_ACCESS_NONE),
|
||||||
|
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = dst_image,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = aspect,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
||||||
|
(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE
|
||||||
|
: VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy);
|
||||||
|
|
||||||
|
const VkBufferImageCopy copy{
|
||||||
|
.bufferOffset = out_offset,
|
||||||
|
.bufferRowLength = 0,
|
||||||
|
.bufferImageHeight = 0,
|
||||||
|
.imageSubresource{
|
||||||
|
.aspectMask = aspect,
|
||||||
|
.mipLevel = 0,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1,
|
||||||
|
},
|
||||||
|
.imageOffset = {0, 0, 0},
|
||||||
|
.imageExtent = extent,
|
||||||
|
};
|
||||||
|
cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
|
||||||
|
|
||||||
|
const VkImageMemoryBarrier post_copy{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = dst_image,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = aspect,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
|
||||||
|
0, {}, {}, post_copy);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|||||||
@@ -164,4 +164,42 @@ private:
|
|||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BlockLinearUnswizzle2DPass final : public ComputePass {
|
||||||
|
public:
|
||||||
|
explicit BlockLinearUnswizzle2DPass(const Device& device_, Scheduler& scheduler_,
|
||||||
|
DescriptorPool& descriptor_pool_,
|
||||||
|
StagingBufferPool& staging_buffer_pool_,
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||||
|
~BlockLinearUnswizzle2DPass();
|
||||||
|
|
||||||
|
[[nodiscard]] static bool IsSupported(const VideoCommon::ImageInfo& info);
|
||||||
|
|
||||||
|
void Unswizzle(Image& image, const StagingBufferRef& swizzled,
|
||||||
|
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Scheduler& scheduler;
|
||||||
|
StagingBufferPool& staging_buffer_pool;
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BlockLinearUnswizzle3DBufferPass final : public ComputePass {
|
||||||
|
public:
|
||||||
|
explicit BlockLinearUnswizzle3DBufferPass(
|
||||||
|
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||||
|
StagingBufferPool& staging_buffer_pool_,
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||||
|
~BlockLinearUnswizzle3DBufferPass();
|
||||||
|
|
||||||
|
[[nodiscard]] static bool IsSupported(const Device& device, const VideoCommon::ImageInfo& info);
|
||||||
|
|
||||||
|
void Unswizzle(Image& image, const StagingBufferRef& swizzled,
|
||||||
|
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Scheduler& scheduler;
|
||||||
|
StagingBufferPool& staging_buffer_pool;
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ using VideoCommon::GenericEnvironment;
|
|||||||
using VideoCommon::GraphicsEnvironment;
|
using VideoCommon::GraphicsEnvironment;
|
||||||
|
|
||||||
constexpr u32 CACHE_VERSION = 18;
|
constexpr u32 CACHE_VERSION = 18;
|
||||||
|
constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128;
|
||||||
|
constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30;
|
||||||
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
|
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
|
||||||
|
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
@@ -699,6 +701,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
|||||||
if (use_vulkan_pipeline_cache) {
|
if (use_vulkan_pipeline_cache) {
|
||||||
SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
|
SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
|
||||||
CACHE_VERSION);
|
CACHE_VERSION);
|
||||||
|
size_t size = 0;
|
||||||
|
vulkan_pipeline_cache.Read(&size, nullptr);
|
||||||
|
last_cache_size.store(size, std::memory_order_relaxed);
|
||||||
|
last_flush = std::chrono::steady_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.statistics) {
|
if (state.statistics) {
|
||||||
@@ -706,6 +712,35 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PipelineCache::QueueVulkanPipelineCacheFlush() {
|
||||||
|
if (!use_vulkan_pipeline_cache || vulkan_pipeline_cache_filename.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (++pipelines_since_flush < VULKAN_CACHE_FLUSH_PIPELINES) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
const auto megabytes = last_cache_size.load(std::memory_order_relaxed) / (1024 * 1024);
|
||||||
|
const std::chrono::seconds interval{
|
||||||
|
std::max<size_t>(VULKAN_CACHE_FLUSH_MIN_SECONDS, megabytes)};
|
||||||
|
if (last_flush.time_since_epoch().count() != 0 && now - last_flush < interval) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (flush_in_flight.exchange(true, std::memory_order_acq_rel)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pipelines_since_flush = 0;
|
||||||
|
last_flush = now;
|
||||||
|
serialization_thread.QueueWork([this] {
|
||||||
|
SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
|
||||||
|
CACHE_VERSION);
|
||||||
|
size_t size = 0;
|
||||||
|
vulkan_pipeline_cache.Read(&size, nullptr);
|
||||||
|
last_cache_size.store(size, std::memory_order_relaxed);
|
||||||
|
flush_in_flight.store(false, std::memory_order_release);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
|
GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
|
||||||
const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)};
|
const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)};
|
||||||
auto& pipeline{pair->second};
|
auto& pipeline{pair->second};
|
||||||
@@ -744,7 +779,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
|||||||
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
|
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
|
||||||
bool build_in_parallel) try {
|
bool build_in_parallel) try {
|
||||||
auto hash = key.Hash();
|
auto hash = key.Hash();
|
||||||
LOG_INFO(Render_Vulkan, "{:#016x}", hash);
|
LOG_DEBUG(Render_Vulkan, "{:#016x}", hash);
|
||||||
size_t env_index{0};
|
size_t env_index{0};
|
||||||
std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
|
std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
|
||||||
const bool uses_vertex_a{key.unique_hashes[0] != 0};
|
const bool uses_vertex_a{key.unique_hashes[0] != 0};
|
||||||
@@ -880,6 +915,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
|
|||||||
}
|
}
|
||||||
SerializePipeline(key, env_ptrs, pipeline_cache_filename, CACHE_VERSION);
|
SerializePipeline(key, env_ptrs, pipeline_cache_filename, CACHE_VERSION);
|
||||||
});
|
});
|
||||||
|
QueueVulkanPipelineCacheFlush();
|
||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -899,6 +935,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
|||||||
SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env_},
|
SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env_},
|
||||||
pipeline_cache_filename, CACHE_VERSION);
|
pipeline_cache_filename, CACHE_VERSION);
|
||||||
});
|
});
|
||||||
|
QueueVulkanPipelineCacheFlush();
|
||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -911,7 +948,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO(Render_Vulkan, "{:#016x}", hash);
|
LOG_DEBUG(Render_Vulkan, "{:#016x}", hash);
|
||||||
|
|
||||||
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
|
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -144,6 +146,8 @@ private:
|
|||||||
vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename,
|
vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename,
|
||||||
u32 expected_cache_version);
|
u32 expected_cache_version);
|
||||||
|
|
||||||
|
void QueueVulkanPipelineCacheFlush();
|
||||||
|
|
||||||
const Device& device;
|
const Device& device;
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
DescriptorPool& descriptor_pool;
|
DescriptorPool& descriptor_pool;
|
||||||
@@ -171,6 +175,10 @@ private:
|
|||||||
|
|
||||||
std::filesystem::path vulkan_pipeline_cache_filename;
|
std::filesystem::path vulkan_pipeline_cache_filename;
|
||||||
vk::PipelineCache vulkan_pipeline_cache;
|
vk::PipelineCache vulkan_pipeline_cache;
|
||||||
|
size_t pipelines_since_flush{};
|
||||||
|
std::chrono::steady_clock::time_point last_flush{};
|
||||||
|
std::atomic<size_t> last_cache_size{};
|
||||||
|
std::atomic_bool flush_in_flight{};
|
||||||
|
|
||||||
Common::ThreadWorker workers;
|
Common::ThreadWorker workers;
|
||||||
Common::ThreadWorker serialization_thread;
|
Common::ThreadWorker serialization_thread;
|
||||||
|
|||||||
@@ -145,18 +145,6 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
|||||||
.preserveAttachmentCount = 0,
|
.preserveAttachmentCount = 0,
|
||||||
.pPreserveAttachments = nullptr,
|
.pPreserveAttachments = nullptr,
|
||||||
};
|
};
|
||||||
const VkSubpassDependency dependency{
|
|
||||||
.srcSubpass = 0, // Current subpass
|
|
||||||
.dstSubpass = 0, // Same subpass (self-dependency)
|
|
||||||
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
|
||||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
|
||||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
|
||||||
.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
|
||||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
|
||||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
||||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
|
||||||
.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
|
|
||||||
};
|
|
||||||
pair->second = device->GetLogical().CreateRenderPass({
|
pair->second = device->GetLogical().CreateRenderPass({
|
||||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
@@ -165,8 +153,8 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
|||||||
.pAttachments = descriptions.empty() ? nullptr : descriptions.data(),
|
.pAttachments = descriptions.empty() ? nullptr : descriptions.data(),
|
||||||
.subpassCount = 1,
|
.subpassCount = 1,
|
||||||
.pSubpasses = &subpass,
|
.pSubpasses = &subpass,
|
||||||
.dependencyCount = 1,
|
.dependencyCount = 0,
|
||||||
.pDependencies = &dependency,
|
.pDependencies = nullptr,
|
||||||
});
|
});
|
||||||
return *pair->second;
|
return *pair->second;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <ankerl/unordered_dense.h>
|
#include <ankerl/unordered_dense.h>
|
||||||
|
|
||||||
|
#include "common/container_hash.h"
|
||||||
#include "video_core/surface.h"
|
#include "video_core/surface.h"
|
||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||||
|
|
||||||
@@ -31,17 +32,26 @@ struct RenderPassKey {
|
|||||||
namespace std {
|
namespace std {
|
||||||
template <>
|
template <>
|
||||||
struct hash<Vulkan::RenderPassKey> {
|
struct hash<Vulkan::RenderPassKey> {
|
||||||
|
static_assert(std::tuple_size_v<decltype(Vulkan::RenderPassKey::color_formats)> <= 8);
|
||||||
|
static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Invalid) <= 0xFF);
|
||||||
|
static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Max) <= 0xFF);
|
||||||
|
static_assert(VK_SAMPLE_COUNT_64_BIT <= 0xFF);
|
||||||
|
|
||||||
[[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept {
|
[[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept {
|
||||||
size_t value = static_cast<size_t>(key.depth_format) << 48;
|
u64 formats = 0;
|
||||||
value ^= static_cast<size_t>(key.samples) << 52;
|
for (size_t index = 0; index < key.color_formats.size(); ++index) {
|
||||||
value ^= static_cast<size_t>(key.resolve_color) << 63;
|
formats |= static_cast<u64>(key.color_formats[index]) << (index * 8);
|
||||||
value ^= static_cast<size_t>(key.color_clear_mask) << 54;
|
|
||||||
value ^= static_cast<size_t>(key.depth_stencil_clear) << 62;
|
|
||||||
value ^= static_cast<size_t>(key.color_discard_mask) << 24;
|
|
||||||
for (size_t i = 0; i < key.color_formats.size(); ++i) {
|
|
||||||
value ^= static_cast<size_t>(key.color_formats[i]) << (i * 6);
|
|
||||||
}
|
}
|
||||||
return value;
|
const u64 state = static_cast<u64>(key.depth_format) |
|
||||||
|
(static_cast<u64>(key.samples) << 8) |
|
||||||
|
(static_cast<u64>(key.color_clear_mask) << 16) |
|
||||||
|
(static_cast<u64>(key.color_discard_mask) << 24) |
|
||||||
|
(static_cast<u64>(key.resolve_color) << 32) |
|
||||||
|
(static_cast<u64>(key.depth_stencil_clear) << 33);
|
||||||
|
size_t seed = 0;
|
||||||
|
Common::HashCombine(seed, formats);
|
||||||
|
Common::HashCombine(seed, state);
|
||||||
|
return seed;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace std
|
} // namespace std
|
||||||
|
|||||||
@@ -628,18 +628,12 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
|
|||||||
.subresourceRange = subresource_range,
|
.subresourceRange = subresource_range,
|
||||||
};
|
};
|
||||||
|
|
||||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
|
||||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
|
||||||
read_barrier);
|
read_barrier);
|
||||||
cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies);
|
cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies);
|
||||||
// TODO: Move this to another API
|
// TODO: Move this to another API
|
||||||
cmdbuf.PipelineBarrier(
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0,
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
nullptr, nullptr, write_barrier);
|
||||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
|
||||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
|
||||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
||||||
0, nullptr, nullptr, write_barrier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region,
|
[[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region,
|
||||||
@@ -933,6 +927,10 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
|
|||||||
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
|
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
|
||||||
staging_buffer_pool, compute_pass_descriptor_queue);
|
staging_buffer_pool, compute_pass_descriptor_queue);
|
||||||
}
|
}
|
||||||
|
bl2d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
||||||
|
compute_pass_descriptor_queue);
|
||||||
|
bl3db_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
||||||
|
compute_pass_descriptor_queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureCacheRuntime::Finish() {
|
void TextureCacheRuntime::Finish() {
|
||||||
@@ -1796,6 +1794,13 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
|
|||||||
}
|
}
|
||||||
flags |= VideoCommon::ImageFlagBits::Converted;
|
flags |= VideoCommon::ImageFlagBits::Converted;
|
||||||
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
|
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
|
||||||
|
} else if (runtime->bl2d_unswizzle_pass && BlockLinearUnswizzle2DPass::IsSupported(info)) {
|
||||||
|
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
|
||||||
|
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
|
||||||
|
} else if (runtime->bl3db_unswizzle_pass &&
|
||||||
|
BlockLinearUnswizzle3DBufferPass::IsSupported(runtime->device, info)) {
|
||||||
|
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
|
||||||
|
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
|
||||||
}
|
}
|
||||||
if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) {
|
if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) {
|
||||||
flags |= VideoCommon::ImageFlagBits::Converted;
|
flags |= VideoCommon::ImageFlagBits::Converted;
|
||||||
@@ -2827,11 +2832,25 @@ VkRenderPass Framebuffer::RenderPassVariant(u32 color_clear_mask, bool depth_ste
|
|||||||
if (color_clear_mask == 0 && !depth_stencil_clear && color_discard_mask == 0) {
|
if (color_clear_mask == 0 && !depth_stencil_clear && color_discard_mask == 0) {
|
||||||
return renderpass;
|
return renderpass;
|
||||||
}
|
}
|
||||||
|
static_assert(NUM_RT <= 8);
|
||||||
|
const u32 variant_key = color_clear_mask | (color_discard_mask << 8) |
|
||||||
|
(static_cast<u32>(depth_stencil_clear) << 16);
|
||||||
|
for (u32 index = 0; index < num_memoized_variants; ++index) {
|
||||||
|
if (variant_keys[index] == variant_key) {
|
||||||
|
return variant_render_passes[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
RenderPassKey key = render_pass_key;
|
RenderPassKey key = render_pass_key;
|
||||||
key.color_clear_mask = color_clear_mask;
|
key.color_clear_mask = color_clear_mask;
|
||||||
key.depth_stencil_clear = depth_stencil_clear;
|
key.depth_stencil_clear = depth_stencil_clear;
|
||||||
key.color_discard_mask = color_discard_mask;
|
key.color_discard_mask = color_discard_mask;
|
||||||
return render_pass_cache->Get(key);
|
const VkRenderPass variant = render_pass_cache->Get(key);
|
||||||
|
if (num_memoized_variants < variant_keys.size()) {
|
||||||
|
variant_keys[num_memoized_variants] = variant_key;
|
||||||
|
variant_render_passes[num_memoized_variants] = variant;
|
||||||
|
++num_memoized_variants;
|
||||||
|
}
|
||||||
|
return variant;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureCacheRuntime::AccelerateImageUpload(
|
void TextureCacheRuntime::AccelerateImageUpload(
|
||||||
@@ -2843,6 +2862,15 @@ void TextureCacheRuntime::AccelerateImageUpload(
|
|||||||
return astc_decoder_pass->Assemble(image, map, swizzles);
|
return astc_decoder_pass->Assemble(image, map, swizzles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bl2d_unswizzle_pass && BlockLinearUnswizzle2DPass::IsSupported(image.info)) {
|
||||||
|
return bl2d_unswizzle_pass->Unswizzle(image, map, swizzles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bl3db_unswizzle_pass &&
|
||||||
|
BlockLinearUnswizzle3DBufferPass::IsSupported(device, image.info)) {
|
||||||
|
return bl3db_unswizzle_pass->Unswizzle(image, map, swizzles);
|
||||||
|
}
|
||||||
|
|
||||||
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
|
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
|
||||||
if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) {
|
if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) {
|
||||||
ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
|
ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
|
||||||
|
|||||||
@@ -149,6 +149,8 @@ public:
|
|||||||
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
||||||
|
|
||||||
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
|
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
|
||||||
|
std::optional<BlockLinearUnswizzle2DPass> bl2d_unswizzle_pass;
|
||||||
|
std::optional<BlockLinearUnswizzle3DBufferPass> bl3db_unswizzle_pass;
|
||||||
const Settings::ResolutionScalingInfo& resolution;
|
const Settings::ResolutionScalingInfo& resolution;
|
||||||
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
|
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
|
||||||
|
|
||||||
@@ -246,6 +248,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr size_t NUM_MEMOIZED_RENDER_PASS_VARIANTS = 8;
|
||||||
|
|
||||||
vk::Framebuffer framebuffer;
|
vk::Framebuffer framebuffer;
|
||||||
VkRenderPass renderpass{};
|
VkRenderPass renderpass{};
|
||||||
VkExtent2D render_area{};
|
VkExtent2D render_area{};
|
||||||
@@ -263,6 +267,9 @@ private:
|
|||||||
RenderPassKey render_pass_key{};
|
RenderPassKey render_pass_key{};
|
||||||
RenderPassCache* render_pass_cache{nullptr};
|
RenderPassCache* render_pass_cache{nullptr};
|
||||||
bool discard_msaa_color{};
|
bool discard_msaa_color{};
|
||||||
|
mutable std::array<u32, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_keys{};
|
||||||
|
mutable std::array<VkRenderPass, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_render_passes{};
|
||||||
|
mutable u32 num_memoized_variants{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class Image : public VideoCommon::ImageBase {
|
class Image : public VideoCommon::ImageBase {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
// 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 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -30,12 +31,62 @@ constexpr u32 pdep(u32 value) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr u32 SWIZZLE_RUN_BYTES = 16;
|
||||||
|
constexpr u32 SWIZZLE_RUN_SHIFT = 4;
|
||||||
|
constexpr u32 SWIZZLE_RUN_MASK = SWIZZLE_RUN_BYTES - 1;
|
||||||
|
constexpr u32 SWIZZLE_RUN_INDEX_MASK = GOB_SIZE_X / SWIZZLE_RUN_BYTES - 1;
|
||||||
|
|
||||||
|
static_assert((SWIZZLE_X_BITS & SWIZZLE_RUN_MASK) == SWIZZLE_RUN_MASK);
|
||||||
|
|
||||||
|
constexpr std::array<u32, GOB_SIZE_X / SWIZZLE_RUN_BYTES> SWIZZLE_X_RUN_TABLE = [] {
|
||||||
|
std::array<u32, GOB_SIZE_X / SWIZZLE_RUN_BYTES> table{};
|
||||||
|
for (u32 index = 0; index < static_cast<u32>(table.size()); ++index) {
|
||||||
|
table[index] = pdep<SWIZZLE_X_BITS>(index << SWIZZLE_RUN_SHIFT);
|
||||||
|
}
|
||||||
|
return table;
|
||||||
|
}();
|
||||||
|
|
||||||
template <u32 mask, u32 incr_amount>
|
template <u32 mask, u32 incr_amount>
|
||||||
void incrpdep(u32& value) {
|
void incrpdep(u32& value) {
|
||||||
static constexpr u32 swizzled_incr = pdep<mask>(incr_amount);
|
static constexpr u32 swizzled_incr = pdep<mask>(incr_amount);
|
||||||
value = ((value | ~mask) + swizzled_incr) & mask;
|
value = ((value | ~mask) + swizzled_incr) & mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <bool TO_LINEAR>
|
||||||
|
void SwizzleRow(std::span<u8> output, std::span<const u8> input, u32 offset_zy, u32 swizzled_y,
|
||||||
|
u32 x_shift, u32 x, u32 num_bytes, u32 linear) {
|
||||||
|
const auto copy = [&](u32 swizzled_x, u32 count) {
|
||||||
|
const u32 swizzled =
|
||||||
|
offset_zy + ((x >> GOB_SIZE_X_SHIFT) << x_shift) + (swizzled_x | swizzled_y);
|
||||||
|
u8* const dst = &output[TO_LINEAR ? swizzled : linear];
|
||||||
|
const u8* const src = &input[TO_LINEAR ? linear : swizzled];
|
||||||
|
std::memcpy(dst, src, count);
|
||||||
|
x += count;
|
||||||
|
linear += count;
|
||||||
|
};
|
||||||
|
|
||||||
|
u32 swizzled_run = SWIZZLE_X_RUN_TABLE[(x >> SWIZZLE_RUN_SHIFT) & SWIZZLE_RUN_INDEX_MASK];
|
||||||
|
u32 remaining = num_bytes;
|
||||||
|
|
||||||
|
const u32 head =
|
||||||
|
(std::min)(SWIZZLE_RUN_BYTES - (x & SWIZZLE_RUN_MASK), remaining) & SWIZZLE_RUN_MASK;
|
||||||
|
if (head != 0) {
|
||||||
|
copy(swizzled_run | (x & SWIZZLE_RUN_MASK), head);
|
||||||
|
remaining -= head;
|
||||||
|
incrpdep<SWIZZLE_X_BITS, SWIZZLE_RUN_BYTES>(swizzled_run);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (remaining >= SWIZZLE_RUN_BYTES) {
|
||||||
|
copy(swizzled_run, SWIZZLE_RUN_BYTES);
|
||||||
|
remaining -= SWIZZLE_RUN_BYTES;
|
||||||
|
incrpdep<SWIZZLE_X_BITS, SWIZZLE_RUN_BYTES>(swizzled_run);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remaining != 0) {
|
||||||
|
copy(swizzled_run, remaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <bool TO_LINEAR, u32 BYTES_PER_PIXEL>
|
template <bool TO_LINEAR, u32 BYTES_PER_PIXEL>
|
||||||
void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth,
|
void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth,
|
||||||
u32 block_height, u32 block_depth, u32 stride) {
|
u32 block_height, u32 block_depth, u32 stride) {
|
||||||
@@ -70,23 +121,9 @@ void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32
|
|||||||
const u32 offset_y = (block_y >> block_height) * block_size +
|
const u32 offset_y = (block_y >> block_height) * block_size +
|
||||||
((block_y & block_height_mask) << GOB_SIZE_SHIFT);
|
((block_y & block_height_mask) << GOB_SIZE_SHIFT);
|
||||||
|
|
||||||
u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL);
|
SwizzleRow<TO_LINEAR>(output, input, offset_z + offset_y, swizzled_y, x_shift,
|
||||||
for (u32 column = 0; column < width;
|
origin_x * BYTES_PER_PIXEL, width * BYTES_PER_PIXEL,
|
||||||
++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) {
|
slice * pitch * height + line * pitch);
|
||||||
const u32 x = (column + origin_x) * BYTES_PER_PIXEL;
|
|
||||||
const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift;
|
|
||||||
|
|
||||||
const u32 base_swizzled_offset = offset_z + offset_y + offset_x;
|
|
||||||
const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y);
|
|
||||||
|
|
||||||
const u32 unswizzled_offset =
|
|
||||||
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
|
|
||||||
|
|
||||||
u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
|
|
||||||
const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
|
|
||||||
|
|
||||||
std::memcpy(dst, src, BYTES_PER_PIXEL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,23 +166,9 @@ void SwizzleSubrectImpl(std::span<u8> output, std::span<const u8> input, u32 wid
|
|||||||
const u32 offset_y = (block_y >> block_height) * block_size +
|
const u32 offset_y = (block_y >> block_height) * block_size +
|
||||||
((block_y & block_height_mask) << GOB_SIZE_SHIFT);
|
((block_y & block_height_mask) << GOB_SIZE_SHIFT);
|
||||||
|
|
||||||
u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL);
|
SwizzleRow<TO_LINEAR>(output, input, offset_z + offset_y, swizzled_y, x_shift,
|
||||||
for (u32 column = 0; column < extent_x;
|
origin_x * BYTES_PER_PIXEL, extent_x * BYTES_PER_PIXEL,
|
||||||
++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) {
|
slice * pitch * height + line * pitch);
|
||||||
const u32 x = (column + origin_x) * BYTES_PER_PIXEL;
|
|
||||||
const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift;
|
|
||||||
|
|
||||||
const u32 base_swizzled_offset = offset_z + offset_y + offset_x;
|
|
||||||
const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y);
|
|
||||||
|
|
||||||
const u32 unswizzled_offset =
|
|
||||||
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
|
|
||||||
|
|
||||||
u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
|
|
||||||
const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
|
|
||||||
|
|
||||||
std::memcpy(dst, src, BYTES_PER_PIXEL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
unprocessed_lines -= lines_in_y;
|
unprocessed_lines -= lines_in_y;
|
||||||
if (unprocessed_lines == 0) {
|
if (unprocessed_lines == 0) {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <ankerl/unordered_dense.h>
|
#include <ankerl/unordered_dense.h>
|
||||||
@@ -16,6 +18,8 @@
|
|||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/fs/fs.h"
|
||||||
|
#include "common/fs/path_util.h"
|
||||||
#include "common/literals.h"
|
#include "common/literals.h"
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
@@ -393,6 +397,17 @@ std::vector<const char*> ExtensionListForVulkan(
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr std::array<char, 8> STATIC_CACHE_MAGIC_NUMBER{'e', 'd', 'e', 'n', 's', 't', 'p', 'c'};
|
||||||
|
constexpr u32 STATIC_CACHE_VERSION = 1;
|
||||||
|
|
||||||
|
std::filesystem::path StaticPipelineCacheFilename() {
|
||||||
|
const auto shader_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
|
||||||
|
if (!Common::FS::CreateDir(shader_dir)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return shader_dir / "vulkan_static_pipelines.bin";
|
||||||
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
void Device::RemoveExtension(bool& extension, const std::string& extension_name) {
|
void Device::RemoveExtension(bool& extension, const std::string& extension_name) {
|
||||||
@@ -780,15 +795,100 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||||||
|
|
||||||
vk::Check(vmaCreateAllocator(&allocator_info, &allocator));
|
vk::Check(vmaCreateAllocator(&allocator_info, &allocator));
|
||||||
|
|
||||||
|
owns_static_pipeline_cache = surface != VkSurfaceKHR{};
|
||||||
|
LoadStaticPipelineCache();
|
||||||
|
|
||||||
// Initialize GPU logging if enabled
|
// Initialize GPU logging if enabled
|
||||||
InitializeGPULogging();
|
InitializeGPULogging();
|
||||||
}
|
}
|
||||||
|
|
||||||
Device::~Device() {
|
Device::~Device() {
|
||||||
|
SaveStaticPipelineCache();
|
||||||
ShutdownGPULogging();
|
ShutdownGPULogging();
|
||||||
vmaDestroyAllocator(allocator);
|
vmaDestroyAllocator(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Device::LoadStaticPipelineCache() {
|
||||||
|
const auto create = [this](size_t size, const void* data) {
|
||||||
|
static_pipeline_cache = logical.CreatePipelineCache({
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.flags = 0,
|
||||||
|
.initialDataSize = size,
|
||||||
|
.pInitialData = data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if (!owns_static_pipeline_cache) {
|
||||||
|
create(0, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto filename = StaticPipelineCacheFilename();
|
||||||
|
if (filename.empty()) {
|
||||||
|
create(0, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::vector<char> data;
|
||||||
|
try {
|
||||||
|
std::ifstream file(filename, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
create(0, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||||
|
const size_t total = static_cast<size_t>(file.tellg());
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
std::array<char, 8> magic{};
|
||||||
|
u32 version{};
|
||||||
|
if (total < magic.size() + sizeof(version)) {
|
||||||
|
create(0, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file.read(magic.data(), magic.size())
|
||||||
|
.read(reinterpret_cast<char*>(&version), sizeof(version));
|
||||||
|
if (magic != STATIC_CACHE_MAGIC_NUMBER || version != STATIC_CACHE_VERSION) {
|
||||||
|
create(0, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data.resize(total - magic.size() - sizeof(version));
|
||||||
|
file.read(data.data(), static_cast<std::streamsize>(data.size()));
|
||||||
|
} catch (const std::ios_base::failure& e) {
|
||||||
|
create(0, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
create(data.size(), data.empty() ? nullptr : data.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::SaveStaticPipelineCache() const {
|
||||||
|
if (!owns_static_pipeline_cache || !static_pipeline_cache) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto filename = StaticPipelineCacheFilename();
|
||||||
|
if (filename.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t size = 0;
|
||||||
|
std::vector<char> data;
|
||||||
|
static_pipeline_cache.Read(&size, nullptr);
|
||||||
|
if (size == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data.resize(size);
|
||||||
|
static_pipeline_cache.Read(&size, data.data());
|
||||||
|
try {
|
||||||
|
std::ofstream file(filename, std::ios::binary | std::ios::trunc);
|
||||||
|
file.exceptions(std::ofstream::failbit);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file.write(STATIC_CACHE_MAGIC_NUMBER.data(), STATIC_CACHE_MAGIC_NUMBER.size())
|
||||||
|
.write(reinterpret_cast<const char*>(&STATIC_CACHE_VERSION),
|
||||||
|
sizeof(STATIC_CACHE_VERSION))
|
||||||
|
.write(data.data(), static_cast<std::streamsize>(size));
|
||||||
|
} catch (const std::ios_base::failure& e) {
|
||||||
|
Common::FS::RemoveFile(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
|
VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
|
||||||
FormatType format_type) const {
|
FormatType format_type) const {
|
||||||
if (IsFormatSupported(wanted_format, wanted_usage, format_type)) {
|
if (IsFormatSupported(wanted_format, wanted_usage, format_type)) {
|
||||||
|
|||||||
@@ -268,6 +268,10 @@ public:
|
|||||||
return physical;
|
return physical;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VkPipelineCache StaticPipelineCache() const noexcept {
|
||||||
|
return *static_pipeline_cache;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the main graphics queue.
|
/// Returns the main graphics queue.
|
||||||
vk::Queue GetGraphicsQueue() const {
|
vk::Queue GetGraphicsQueue() const {
|
||||||
return graphics_queue;
|
return graphics_queue;
|
||||||
@@ -1088,6 +1092,9 @@ private:
|
|||||||
/// Returns true if the device natively supports blitting depth stencil images.
|
/// Returns true if the device natively supports blitting depth stencil images.
|
||||||
bool TestDepthStencilBlits(VkFormat format) const;
|
bool TestDepthStencilBlits(VkFormat format) const;
|
||||||
|
|
||||||
|
void LoadStaticPipelineCache();
|
||||||
|
void SaveStaticPipelineCache() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VkInstance instance; ///< Vulkan instance.
|
VkInstance instance; ///< Vulkan instance.
|
||||||
VmaAllocator allocator; ///< VMA allocator.
|
VmaAllocator allocator; ///< VMA allocator.
|
||||||
@@ -1096,6 +1103,8 @@ private:
|
|||||||
vk::Device logical; ///< Logical device.
|
vk::Device logical; ///< Logical device.
|
||||||
vk::Queue graphics_queue; ///< Main graphics queue.
|
vk::Queue graphics_queue; ///< Main graphics queue.
|
||||||
vk::Queue present_queue; ///< Main present queue.
|
vk::Queue present_queue; ///< Main present queue.
|
||||||
|
vk::PipelineCache static_pipeline_cache;
|
||||||
|
bool owns_static_pipeline_cache{};
|
||||||
u32 instance_version{}; ///< Vulkan instance version.
|
u32 instance_version{}; ///< Vulkan instance version.
|
||||||
u32 graphics_family{}; ///< Main graphics queue family index.
|
u32 graphics_family{}; ///< Main graphics queue family index.
|
||||||
u32 present_family{}; ///< Main present queue family index.
|
u32 present_family{}; ///< Main present queue family index.
|
||||||
|
|||||||
Reference in New Issue
Block a user