mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-29 09:58:05 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 02fef4418d | |||
| 064ab33c08 | |||
| 68e1048194 | |||
| 024f7d4918 | |||
| 96fc3378a4 | |||
| 4088d44789 | |||
| 8be2e5dfd8 | |||
| a042cedc6b | |||
| c65694e3ec | |||
| b94ec40989 | |||
| 357e2e7109 | |||
| 930989ab82 | |||
| 8cbe8d1ce9 | |||
| ff596b6e8a | |||
| 8d2d61bd8c | |||
| d0b135afb5 | |||
| 159841a6fc | |||
| 2ea449f71a | |||
| 602756e990 | |||
| 34f9c0fdec |
@@ -20,8 +20,6 @@ namespace VideoCommon {
|
||||
|
||||
enum class BufferFlagBits {
|
||||
Picked = 1 << 0,
|
||||
CachedWrites = 1 << 1,
|
||||
PreemtiveDownload = 1 << 2,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(BufferFlagBits)
|
||||
|
||||
@@ -58,15 +56,6 @@ public:
|
||||
flags |= BufferFlagBits::Picked;
|
||||
}
|
||||
|
||||
void MarkPreemtiveDownload() noexcept {
|
||||
flags |= BufferFlagBits::PreemtiveDownload;
|
||||
}
|
||||
|
||||
/// Unmark buffer as picked
|
||||
void Unpick() noexcept {
|
||||
flags &= ~BufferFlagBits::Picked;
|
||||
}
|
||||
|
||||
/// Increases the likeliness of this being a stream buffer
|
||||
void IncreaseStreamScore(int score) noexcept {
|
||||
stream_score += score;
|
||||
@@ -87,15 +76,6 @@ public:
|
||||
return True(flags & BufferFlagBits::Picked);
|
||||
}
|
||||
|
||||
/// Returns true when the buffer has pending cached writes
|
||||
[[nodiscard]] bool HasCachedWrites() const noexcept {
|
||||
return True(flags & BufferFlagBits::CachedWrites);
|
||||
}
|
||||
|
||||
bool IsPreemtiveDownload() const noexcept {
|
||||
return True(flags & BufferFlagBits::PreemtiveDownload);
|
||||
}
|
||||
|
||||
/// Returns the base CPU address of the buffer
|
||||
[[nodiscard]] VAddr CpuAddr() const noexcept {
|
||||
return cpu_addr;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
|
||||
@@ -121,25 +122,6 @@ void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
||||
memory_tracker.MarkRegionAsCpuModified(device_addr, size);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::CachedWriteMemory(DAddr device_addr, u64 size) {
|
||||
const bool is_dirty = IsRegionRegistered(device_addr, size);
|
||||
if (!is_dirty) {
|
||||
return;
|
||||
}
|
||||
DAddr aligned_start = Common::AlignDown(device_addr, DEVICE_PAGESIZE);
|
||||
DAddr aligned_end = Common::AlignUp(device_addr + size, DEVICE_PAGESIZE);
|
||||
if (!IsRegionGpuModified(aligned_start, aligned_end - aligned_start)) {
|
||||
WriteMemory(device_addr, size);
|
||||
return;
|
||||
}
|
||||
|
||||
tmp_buffer.resize_destructive(size);
|
||||
device_memory.ReadBlockUnsafe(device_addr, tmp_buffer.data(), size);
|
||||
|
||||
InlineMemoryImplementation(device_addr, size, tmp_buffer);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::OnCPUWrite(DAddr device_addr, u64 size) {
|
||||
const bool is_dirty = IsRegionRegistered(device_addr, size);
|
||||
@@ -422,7 +404,7 @@ void BufferCache<P>::UnbindGraphicsStorageBuffers(size_t stage) {
|
||||
}
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index,
|
||||
void BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index,
|
||||
u32 cbuf_offset, bool is_written) {
|
||||
const bool already_enabled =
|
||||
((channel_state->enabled_storage_buffers[stage] >> ssbo_index) & 1U) != 0;
|
||||
@@ -433,7 +415,7 @@ bool BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index,
|
||||
LOG_WARNING(HW_GPU,
|
||||
"Skipping graphics storage buffer {} due to driver limit {}",
|
||||
ssbo_index, max_bindings);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -449,7 +431,6 @@ bool BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index,
|
||||
const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset;
|
||||
channel_state->storage_buffers[stage][ssbo_index] =
|
||||
StorageBufferBinding(ssbo_addr, cbuf_index, is_written);
|
||||
return (channel_state->storage_buffers[stage][ssbo_index].buffer_id != NULL_BUFFER_ID);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -762,16 +743,6 @@ void BufferCache<P>::BindHostIndexBuffer() {
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindHostVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size,
|
||||
u32 stride) {
|
||||
if constexpr (IS_OPENGL) {
|
||||
runtime.BindVertexBuffer(index, buffer, offset, size, stride);
|
||||
} else {
|
||||
runtime.BindVertexBuffer(index, buffer.Handle(), offset, size, stride);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
Binding& BufferCache<P>::VertexBufferSlot(u32 index) {
|
||||
ASSERT(index < NUM_VERTEX_BUFFERS);
|
||||
@@ -1251,9 +1222,14 @@ void BufferCache<P>::UpdateIndexBuffer() {
|
||||
const GPUVAddr gpu_addr_begin = index_buffer_ref.StartAddress();
|
||||
const GPUVAddr gpu_addr_end = index_buffer_ref.EndAddress();
|
||||
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
|
||||
const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
|
||||
const u32 draw_size = (index_buffer_ref.count + index_buffer_ref.first) * u32(index_buffer_ref.FormatSizeInBytes());
|
||||
const u32 size = (std::min)(address_size, draw_size);
|
||||
u64 address_size = 0;
|
||||
if (gpu_addr_end > gpu_addr_begin) {
|
||||
address_size = (std::min)(gpu_addr_end - gpu_addr_begin,
|
||||
u64{(std::numeric_limits<u32>::max)()});
|
||||
}
|
||||
const u64 draw_size = (u64{index_buffer_ref.count} + u64{index_buffer_ref.first}) *
|
||||
u64{index_buffer_ref.FormatSizeInBytes()};
|
||||
const u32 size = static_cast<u32>((std::min)(address_size, draw_size));
|
||||
if (size == 0 || !device_addr) {
|
||||
channel_state->index_buffer = NULL_BINDING;
|
||||
return;
|
||||
@@ -1268,6 +1244,20 @@ void BufferCache<P>::UpdateIndexBuffer() {
|
||||
template <class P>
|
||||
void BufferCache<P>::UpdateVertexBuffers() {
|
||||
auto& flags = maxwell3d->dirty.flags;
|
||||
const u32 base_instance = maxwell3d->draw_manager.draw_state.base_instance;
|
||||
if (draw_instance_count != last_draw_instance_count ||
|
||||
base_instance != last_draw_base_instance) {
|
||||
last_draw_instance_count = draw_instance_count;
|
||||
last_draw_base_instance = base_instance;
|
||||
const auto& instances = maxwell3d->regs.vertex_stream_instances;
|
||||
for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
|
||||
if (!instances.IsInstancingEnabled(index)) {
|
||||
continue;
|
||||
}
|
||||
flags[Dirty::VertexBuffer0 + index] = true;
|
||||
flags[Dirty::VertexBuffers] = true;
|
||||
}
|
||||
}
|
||||
if (!maxwell3d->dirty.flags[Dirty::VertexBuffers]) {
|
||||
return;
|
||||
}
|
||||
@@ -1288,15 +1278,37 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
|
||||
const GPUVAddr gpu_addr_begin = array.Address();
|
||||
const GPUVAddr gpu_addr_end = limit.Address() + 1;
|
||||
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
|
||||
const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
|
||||
u32 size = address_size; // TODO: Analyze stride and number of vertices
|
||||
if (array.enable == 0 || size == 0 || !device_addr) {
|
||||
if (array.enable == 0 || !device_addr || gpu_addr_end <= gpu_addr_begin) {
|
||||
channel_state->vertex_buffers[index] = NULL_BINDING;
|
||||
UpdateVertexBufferSlot(index, NULL_BINDING);
|
||||
return;
|
||||
}
|
||||
if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || size >= 64_MiB) {
|
||||
size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size));
|
||||
// TODO: Analyze stride and number of vertices
|
||||
constexpr u64 implausible_size = 64_MiB;
|
||||
u64 address_size = gpu_addr_end - gpu_addr_begin;
|
||||
if (address_size > u64{(std::numeric_limits<u32>::max)()}) {
|
||||
address_size = implausible_size;
|
||||
}
|
||||
const bool is_instanced = maxwell3d->regs.vertex_stream_instances.IsInstancingEnabled(index);
|
||||
const u64 stride = static_cast<u64>(array.stride);
|
||||
if (is_instanced && stride != 0 && draw_instance_count != 0) {
|
||||
const u64 base_instance = static_cast<u64>(maxwell3d->draw_manager.draw_state.base_instance);
|
||||
u64 elements = base_instance + 1;
|
||||
if (array.frequency != 0) {
|
||||
elements = (base_instance + static_cast<u64>(draw_instance_count) - 1) /
|
||||
static_cast<u64>(array.frequency) +
|
||||
1;
|
||||
}
|
||||
address_size = (std::min)(address_size, (elements + 1) * stride);
|
||||
}
|
||||
if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || address_size >= implausible_size) {
|
||||
address_size = gpu_memory->MaxContinuousRange(gpu_addr_begin, address_size);
|
||||
}
|
||||
const u32 size = static_cast<u32>(address_size);
|
||||
if (size == 0) {
|
||||
channel_state->vertex_buffers[index] = NULL_BINDING;
|
||||
UpdateVertexBufferSlot(index, NULL_BINDING);
|
||||
return;
|
||||
}
|
||||
const BufferId buffer_id = FindBuffer(*device_addr, size);
|
||||
const Binding binding{
|
||||
@@ -1578,9 +1590,10 @@ template <class P>
|
||||
BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size) {
|
||||
DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE);
|
||||
device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE);
|
||||
wanted_size = static_cast<u32>(device_addr_end - device_addr);
|
||||
constexpr u64 max_buffer_size = u64{(std::numeric_limits<u32>::max)()};
|
||||
wanted_size = static_cast<u32>((std::min)(device_addr_end - device_addr, max_buffer_size));
|
||||
const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size);
|
||||
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
|
||||
const u32 size = static_cast<u32>((std::min)(overlap.end - overlap.begin, max_buffer_size));
|
||||
const BufferId new_buffer_id = slot_buffers.insert(runtime, overlap.begin, size);
|
||||
auto& new_buffer = slot_buffers[new_buffer_id];
|
||||
const size_t size_bytes = new_buffer.SizeBytes();
|
||||
|
||||
@@ -217,8 +217,6 @@ public:
|
||||
|
||||
void WriteMemory(DAddr device_addr, u64 size);
|
||||
|
||||
void CachedWriteMemory(DAddr device_addr, u64 size);
|
||||
|
||||
bool OnCPUWrite(DAddr device_addr, u64 size);
|
||||
|
||||
void DownloadMemory(DAddr device_addr, u64 size);
|
||||
@@ -248,7 +246,7 @@ public:
|
||||
|
||||
void UnbindGraphicsStorageBuffers(size_t stage);
|
||||
|
||||
bool BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
|
||||
void BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
|
||||
bool is_written);
|
||||
|
||||
void UnbindGraphicsTextureBuffers(size_t stage);
|
||||
@@ -309,6 +307,10 @@ public:
|
||||
current_draw_indirect = current_draw_indirect_;
|
||||
}
|
||||
|
||||
void SetDrawInstanceCount(u32 draw_instance_count_) {
|
||||
draw_instance_count = draw_instance_count_;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectCount();
|
||||
|
||||
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer();
|
||||
@@ -376,8 +378,6 @@ private:
|
||||
|
||||
void BindHostTransformFeedbackBuffers();
|
||||
|
||||
void BindHostVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride);
|
||||
|
||||
void BindHostComputeUniformBuffers();
|
||||
|
||||
void BindHostComputeStorageBuffers();
|
||||
@@ -484,6 +484,10 @@ private:
|
||||
|
||||
const Tegra::Engines::Maxwell3D::DrawManager::IndirectParams* current_draw_indirect{};
|
||||
|
||||
u32 draw_instance_count = 0;
|
||||
u32 last_draw_instance_count = 0;
|
||||
u32 last_draw_base_instance = 0;
|
||||
|
||||
u32 last_index_count = 0;
|
||||
|
||||
u32 enabled_vertex_buffers_mask = 0;
|
||||
|
||||
@@ -206,6 +206,40 @@ foreach(VARIANT IN ITEMS ${SHADER_TYPE_VARIANTS})
|
||||
set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE})
|
||||
endforeach()
|
||||
|
||||
set(SHADER_DEFINE_VARIANTS
|
||||
"block_linear_unswizzle_2d.comp|nonarrow|HAS_EXTENDED_TYPES=0"
|
||||
"pitch_unswizzle.comp|nonarrow|HAS_EXTENDED_TYPES=0"
|
||||
"block_linear_unswizzle_3d.comp|nonarrow|HAS_EXTENDED_TYPES=0"
|
||||
)
|
||||
|
||||
foreach(VARIANT IN ITEMS ${SHADER_DEFINE_VARIANTS})
|
||||
string(REPLACE "|" ";" VARIANT_PARTS ${VARIANT})
|
||||
list(GET VARIANT_PARTS 0 VARIANT_FILENAME)
|
||||
list(GET VARIANT_PARTS 1 VARIANT_SUFFIX)
|
||||
list(GET VARIANT_PARTS 2 VARIANT_DEFINE)
|
||||
|
||||
set(VARIANT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${VARIANT_FILENAME})
|
||||
get_filename_component(VARIANT_STEM ${VARIANT_FILENAME} NAME_WE)
|
||||
get_filename_component(VARIANT_EXT ${VARIANT_FILENAME} EXT)
|
||||
string(REPLACE "." "" VARIANT_EXT ${VARIANT_EXT})
|
||||
set(VARIANT_NAME ${VARIANT_STEM}_${VARIANT_SUFFIX}_${VARIANT_EXT})
|
||||
|
||||
string(TOUPPER ${VARIANT_NAME}_SPV VARIANT_VARIABLE_NAME)
|
||||
set(VARIANT_HEADER_FILE ${SHADER_DIR}/${VARIANT_NAME}_spv.h)
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${VARIANT_HEADER_FILE}
|
||||
COMMAND
|
||||
${GLSLANGVALIDATOR} -V ${QUIET_FLAG} -I"${FIDELITYFX_INCLUDE_DIR}" ${GLSL_FLAGS}
|
||||
-D${VARIANT_DEFINE}
|
||||
--variable-name ${VARIANT_VARIABLE_NAME} -o ${VARIANT_HEADER_FILE} ${VARIANT_SOURCE}
|
||||
--target-env ${SPIR_V_VERSION}
|
||||
MAIN_DEPENDENCY
|
||||
${VARIANT_SOURCE}
|
||||
)
|
||||
set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE})
|
||||
endforeach()
|
||||
|
||||
foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
|
||||
get_filename_component(FILENAME ${FILEPATH} NAME)
|
||||
string(REPLACE "." "_" HEADER_NAME ${FILENAME})
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
|
||||
#ifdef VULKAN
|
||||
|
||||
#ifndef HAS_EXTENDED_TYPES
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#endif
|
||||
#if HAS_EXTENDED_TYPES
|
||||
#extension GL_EXT_shader_16bit_storage : require
|
||||
#extension GL_EXT_shader_8bit_storage : require
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#endif
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
|
||||
#ifdef VULKAN
|
||||
|
||||
#ifndef HAS_EXTENDED_TYPES
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#endif
|
||||
#if HAS_EXTENDED_TYPES
|
||||
#extension GL_EXT_shader_16bit_storage : require
|
||||
#extension GL_EXT_shader_8bit_storage : require
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#endif
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
|
||||
#ifdef VULKAN
|
||||
|
||||
#ifndef HAS_EXTENDED_TYPES
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#endif
|
||||
#if HAS_EXTENDED_TYPES
|
||||
#extension GL_EXT_shader_16bit_storage : require
|
||||
#extension GL_EXT_shader_8bit_storage : require
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#endif
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
|
||||
@@ -226,22 +226,6 @@ void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) {
|
||||
}
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size,
|
||||
u32 stride) {
|
||||
if (index >= max_attributes) {
|
||||
return;
|
||||
}
|
||||
if (has_unified_vertex_buffers) {
|
||||
buffer.MakeResident(GL_READ_ONLY);
|
||||
glBindVertexBuffer(index, 0, 0, static_cast<GLsizei>(stride));
|
||||
glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, index,
|
||||
buffer.HostGpuAddr() + offset, static_cast<GLsizeiptr>(size));
|
||||
} else {
|
||||
glBindVertexBuffer(index, buffer.Handle(), static_cast<GLintptr>(offset),
|
||||
static_cast<GLsizei>(stride));
|
||||
}
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
|
||||
// TODO: Should HostBindings provide the correct runtime types to avoid these transforms?
|
||||
std::array<GLuint, 32> buffer_handles;
|
||||
|
||||
@@ -99,8 +99,6 @@ public:
|
||||
|
||||
void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size);
|
||||
|
||||
void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride);
|
||||
|
||||
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
||||
|
||||
void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size);
|
||||
|
||||
@@ -259,6 +259,7 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
|
||||
buffer_cache.SetDrawInstanceCount(instance_count);
|
||||
PrepareDraw(is_indexed, [this, is_indexed, instance_count](GLenum primitive_mode) {
|
||||
const auto& draw_state = maxwell3d->draw_manager.draw_state;
|
||||
const GLuint base_instance = GLuint(draw_state.base_instance);
|
||||
@@ -304,6 +305,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
|
||||
void RasterizerOpenGL::DrawIndirect() {
|
||||
const auto& params = maxwell3d->draw_manager.indirect_state;
|
||||
buffer_cache.SetDrawIndirect(¶ms);
|
||||
buffer_cache.SetDrawInstanceCount(0);
|
||||
PrepareDraw(params.is_indexed, [this, ¶ms](GLenum primitive_mode) {
|
||||
if (params.is_byte_count) {
|
||||
const GPUVAddr tfb_object_base_addr = params.indirect_start_address - 4U;
|
||||
|
||||
@@ -585,29 +585,6 @@ void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 fir
|
||||
}
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride) {
|
||||
if (index >= device.GetMaxVertexInputBindings()) {
|
||||
return;
|
||||
}
|
||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||
scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) {
|
||||
const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0;
|
||||
const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE;
|
||||
const VkDeviceSize vk_stride = stride;
|
||||
cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride);
|
||||
});
|
||||
} else {
|
||||
if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) {
|
||||
ReserveNullBuffer();
|
||||
buffer = *null_buffer;
|
||||
offset = 0;
|
||||
}
|
||||
scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.BindVertexBuffer(index, buffer, offset);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
|
||||
boost::container::static_vector<VkBuffer, VideoCommon::NUM_VERTEX_BUFFERS> buffer_handles(bindings.buffers.size());
|
||||
for (u32 i = 0; i < bindings.buffers.size(); ++i) {
|
||||
|
||||
@@ -138,8 +138,6 @@ public:
|
||||
|
||||
void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
|
||||
|
||||
void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride);
|
||||
|
||||
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
||||
|
||||
void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);
|
||||
|
||||
@@ -22,7 +22,13 @@
|
||||
#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_uint8_comp_spv.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_2d_comp_spv.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_2d_nonarrow_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_comp_spv.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_nonarrow_comp_spv.h"
|
||||
#include "video_core/host_shaders/pitch_unswizzle_comp_spv.h"
|
||||
#include "video_core/host_shaders/pitch_unswizzle_nonarrow_comp_spv.h"
|
||||
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||
@@ -872,4 +878,291 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
|
||||
});
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u32 UNSWIZZLE_BINDING_INPUT_BUFFER = 0;
|
||||
constexpr u32 UNSWIZZLE_BINDING_OUTPUT_IMAGE = 1;
|
||||
constexpr size_t UNSWIZZLE_NUM_BINDINGS = 2;
|
||||
|
||||
constexpr std::array<VkDescriptorSetLayoutBinding, UNSWIZZLE_NUM_BINDINGS>
|
||||
UNSWIZZLE_DESCRIPTOR_SET_BINDINGS{{
|
||||
{
|
||||
.binding = UNSWIZZLE_BINDING_INPUT_BUFFER,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
{
|
||||
.binding = UNSWIZZLE_BINDING_OUTPUT_IMAGE,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
}};
|
||||
|
||||
constexpr std::array<VkDescriptorUpdateTemplateEntry, UNSWIZZLE_NUM_BINDINGS>
|
||||
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE{{
|
||||
{
|
||||
.dstBinding = UNSWIZZLE_BINDING_INPUT_BUFFER,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = UNSWIZZLE_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
},
|
||||
{
|
||||
.dstBinding = UNSWIZZLE_BINDING_OUTPUT_IMAGE,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.offset = UNSWIZZLE_BINDING_OUTPUT_IMAGE * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
},
|
||||
}};
|
||||
|
||||
constexpr DescriptorBankInfo UNSWIZZLE_BANK_INFO{
|
||||
.uniform_buffers = 0,
|
||||
.storage_buffers = 1,
|
||||
.texture_buffers = 0,
|
||||
.image_buffers = 0,
|
||||
.textures = 0,
|
||||
.images = 1,
|
||||
.score = 2,
|
||||
};
|
||||
|
||||
[[nodiscard]] std::span<const u32> UnswizzleSpv(const Device& device,
|
||||
std::span<const u32> extended,
|
||||
std::span<const u32> narrow) {
|
||||
if (device.IsStorageBuffer8BitAccessSupported() &&
|
||||
device.IsStorageBuffer16BitAccessSupported()) {
|
||||
return extended;
|
||||
}
|
||||
return narrow;
|
||||
}
|
||||
|
||||
struct PitchUnswizzlePushConstants {
|
||||
alignas(8) std::array<u32, 2> origin;
|
||||
alignas(8) std::array<s32, 2> destination;
|
||||
u32 bytes_per_block;
|
||||
u32 pitch;
|
||||
};
|
||||
|
||||
void RecordUnswizzleEntryBarrier(Scheduler& scheduler, VkPipeline vk_pipeline, VkImage vk_image,
|
||||
VkImageAspectFlags aspect_mask, bool is_initialized) {
|
||||
scheduler.Record([vk_pipeline, vk_image, aspect_mask,
|
||||
is_initialized](vk::CommandBuffer cmdbuf) {
|
||||
VkAccessFlags src_access = VK_ACCESS_NONE;
|
||||
VkImageLayout old_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
if (is_initialized) {
|
||||
src_access = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
old_layout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
}
|
||||
const VkImageMemoryBarrier image_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = src_access,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||
.oldLayout = old_layout,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = vk_image,
|
||||
.subresourceRange{
|
||||
.aspectMask = aspect_mask,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
VkPipelineStageFlags src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
if (is_initialized) {
|
||||
src_stage = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER;
|
||||
}
|
||||
cmdbuf.PipelineBarrier(src_stage, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
|
||||
});
|
||||
}
|
||||
|
||||
void RecordUnswizzleExitBarrier(Scheduler& scheduler, VkImage vk_image,
|
||||
VkImageAspectFlags aspect_mask) {
|
||||
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
|
||||
const VkImageMemoryBarrier image_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = vk_image,
|
||||
.subresourceRange{
|
||||
.aspectMask = aspect_mask,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 0, image_barrier);
|
||||
});
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||
: ComputePass(device_, scheduler_, descriptor_pool_, UNSWIZZLE_DESCRIPTOR_SET_BINDINGS,
|
||||
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE, UNSWIZZLE_BANK_INFO,
|
||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(
|
||||
VideoCommon::Accelerated::BlockLinearSwizzle2DParams)>,
|
||||
UnswizzleSpv(device_, BLOCK_LINEAR_UNSWIZZLE_2D_COMP_SPV,
|
||||
BLOCK_LINEAR_UNSWIZZLE_2D_NONARROW_COMP_SPV)),
|
||||
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||
|
||||
BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default;
|
||||
|
||||
void BlockLinearUnswizzle2DPass::Unswizzle(
|
||||
Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||
using namespace VideoCommon::Accelerated;
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
const VkPipeline vk_pipeline = *pipeline;
|
||||
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||
const VkImage vk_image = image.Handle();
|
||||
const bool is_initialized = image.ExchangeInitialization();
|
||||
RecordUnswizzleEntryBarrier(scheduler, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
||||
|
||||
const u32 num_layers = static_cast<u32>(image.info.resources.layers);
|
||||
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
|
||||
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U);
|
||||
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||
image.guest_size_bytes - swizzle.buffer_offset);
|
||||
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
||||
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||
|
||||
const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
|
||||
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_layers, params,
|
||||
descriptor_data](vk::CommandBuffer cmdbuf) {
|
||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
||||
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_layers);
|
||||
});
|
||||
}
|
||||
RecordUnswizzleExitBarrier(scheduler, vk_image, aspect_mask);
|
||||
}
|
||||
|
||||
BlockLinearUnswizzleImage3DPass::BlockLinearUnswizzleImage3DPass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||
: ComputePass(device_, scheduler_, descriptor_pool_, UNSWIZZLE_DESCRIPTOR_SET_BINDINGS,
|
||||
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE, UNSWIZZLE_BANK_INFO,
|
||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearSwizzle3DParams)>,
|
||||
UnswizzleSpv(device_, BLOCK_LINEAR_UNSWIZZLE_3D_COMP_SPV,
|
||||
BLOCK_LINEAR_UNSWIZZLE_3D_NONARROW_COMP_SPV)),
|
||||
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||
|
||||
BlockLinearUnswizzleImage3DPass::~BlockLinearUnswizzleImage3DPass() = default;
|
||||
|
||||
void BlockLinearUnswizzleImage3DPass::Unswizzle(
|
||||
Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||
using namespace VideoCommon::Accelerated;
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
const VkPipeline vk_pipeline = *pipeline;
|
||||
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||
const VkImage vk_image = image.Handle();
|
||||
const bool is_initialized = image.ExchangeInitialization();
|
||||
RecordUnswizzleEntryBarrier(scheduler, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
||||
|
||||
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 16U);
|
||||
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
|
||||
const u32 num_dispatches_z = Common::DivCeil(swizzle.num_tiles.depth, 8U);
|
||||
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||
image.guest_size_bytes - swizzle.buffer_offset);
|
||||
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
||||
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||
|
||||
const auto params = MakeBlockLinearSwizzle3DParams(swizzle, image.info);
|
||||
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z, params,
|
||||
descriptor_data](vk::CommandBuffer cmdbuf) {
|
||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
||||
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_dispatches_z);
|
||||
});
|
||||
}
|
||||
RecordUnswizzleExitBarrier(scheduler, vk_image, aspect_mask);
|
||||
}
|
||||
|
||||
PitchUnswizzlePass::PitchUnswizzlePass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||
: ComputePass(device_, scheduler_, descriptor_pool_, UNSWIZZLE_DESCRIPTOR_SET_BINDINGS,
|
||||
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE, UNSWIZZLE_BANK_INFO,
|
||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(PitchUnswizzlePushConstants)>,
|
||||
UnswizzleSpv(device_, PITCH_UNSWIZZLE_COMP_SPV,
|
||||
PITCH_UNSWIZZLE_NONARROW_COMP_SPV)),
|
||||
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||
|
||||
PitchUnswizzlePass::~PitchUnswizzlePass() = default;
|
||||
|
||||
void PitchUnswizzlePass::Unswizzle(Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
const VkPipeline vk_pipeline = *pipeline;
|
||||
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||
const VkImage vk_image = image.Handle();
|
||||
const bool is_initialized = image.ExchangeInitialization();
|
||||
RecordUnswizzleEntryBarrier(scheduler, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
||||
|
||||
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image.info.format);
|
||||
const u32 pitch = image.info.pitch;
|
||||
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
|
||||
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U);
|
||||
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||
image.guest_size_bytes - swizzle.buffer_offset);
|
||||
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
||||
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||
|
||||
const PitchUnswizzlePushConstants params{
|
||||
.origin{0, 0},
|
||||
.destination{0, 0},
|
||||
.bytes_per_block = bytes_per_block,
|
||||
.pitch = pitch,
|
||||
};
|
||||
scheduler.Record([this, num_dispatches_x, num_dispatches_y, params,
|
||||
descriptor_data](vk::CommandBuffer cmdbuf) {
|
||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
||||
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, 1);
|
||||
});
|
||||
}
|
||||
RecordUnswizzleExitBarrier(scheduler, vk_image, aspect_mask);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -164,4 +164,49 @@ private:
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||
};
|
||||
|
||||
class BlockLinearUnswizzle2DPass final : public ComputePass {
|
||||
public:
|
||||
explicit BlockLinearUnswizzle2DPass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||
~BlockLinearUnswizzle2DPass();
|
||||
|
||||
void Unswizzle(Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||
|
||||
private:
|
||||
Scheduler& scheduler;
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||
};
|
||||
|
||||
class BlockLinearUnswizzleImage3DPass final : public ComputePass {
|
||||
public:
|
||||
explicit BlockLinearUnswizzleImage3DPass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||
~BlockLinearUnswizzleImage3DPass();
|
||||
|
||||
void Unswizzle(Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||
|
||||
private:
|
||||
Scheduler& scheduler;
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||
};
|
||||
|
||||
class PitchUnswizzlePass final : public ComputePass {
|
||||
public:
|
||||
explicit PitchUnswizzlePass(const Device& device_, Scheduler& scheduler_,
|
||||
DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||
~PitchUnswizzlePass();
|
||||
|
||||
void Unswizzle(Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||
|
||||
private:
|
||||
Scheduler& scheduler;
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -694,9 +694,11 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
const size_t num_vertex_arrays = (std::min)(
|
||||
Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
|
||||
for (size_t index = 0; index < num_vertex_arrays; ++index) {
|
||||
const bool instanced = key.state.binding_divisors[index] != 0;
|
||||
const auto rate =
|
||||
instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
const bool instanced = ((key.state.enabled_divisors >> index) & 1) != 0;
|
||||
auto rate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
if (instanced) {
|
||||
rate = VK_VERTEX_INPUT_RATE_INSTANCE;
|
||||
}
|
||||
vertex_bindings.push_back({
|
||||
.binding = static_cast<u32>(index),
|
||||
.stride = key.state.vertex_strides[index],
|
||||
@@ -705,7 +707,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
if (instanced) {
|
||||
vertex_binding_divisors.push_back({
|
||||
.binding = static_cast<u32>(index),
|
||||
.divisor = key.state.binding_divisors[index],
|
||||
.divisor = device.GetVertexAttribDivisor(key.state.binding_divisors[index]),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,6 +260,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
|
||||
}
|
||||
|
||||
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
|
||||
buffer_cache.SetDrawInstanceCount(instance_count);
|
||||
PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
|
||||
const auto& draw_state = maxwell3d->draw_manager.draw_state;
|
||||
const u32 num_instances{instance_count};
|
||||
@@ -295,6 +296,7 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
|
||||
void RasterizerVulkan::DrawIndirect() {
|
||||
const auto& params = maxwell3d->draw_manager.indirect_state;
|
||||
buffer_cache.SetDrawIndirect(¶ms);
|
||||
buffer_cache.SetDrawInstanceCount(0);
|
||||
PrepareDraw(params.is_indexed, [this, ¶ms] {
|
||||
const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer();
|
||||
const auto& buffer = indirect_buffer.first;
|
||||
@@ -1917,13 +1919,19 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
|
||||
for (u32 binding = 0; binding < max_bindings; ++binding) {
|
||||
const auto& input_binding{regs.vertex_streams[binding]};
|
||||
const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)};
|
||||
auto input_rate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
u32 divisor = 1;
|
||||
if (is_instanced) {
|
||||
input_rate = VK_VERTEX_INPUT_RATE_INSTANCE;
|
||||
divisor = device.GetVertexAttribDivisor(input_binding.frequency);
|
||||
}
|
||||
bindings.push_back({
|
||||
.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT,
|
||||
.pNext = nullptr,
|
||||
.binding = binding,
|
||||
.stride = input_binding.stride,
|
||||
.inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX,
|
||||
.divisor = is_instanced ? input_binding.frequency : 1,
|
||||
.inputRate = input_rate,
|
||||
.divisor = divisor,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -449,7 +449,9 @@ void Scheduler::EndRenderPass()
|
||||
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
|
||||
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
|
||||
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
|
||||
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
|
||||
| VK_ACCESS_TRANSFER_READ_BIT
|
||||
| VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
@@ -460,7 +462,7 @@ void Scheduler::EndRenderPass()
|
||||
}
|
||||
cmdbuf.EndRenderPass();
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
|
||||
0, nullptr, nullptr, vk::Span(barriers.data(), num_images));
|
||||
if (has_transform_feedback) {
|
||||
static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{
|
||||
|
||||
@@ -160,6 +160,55 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
info.size.depth == 1;
|
||||
}
|
||||
|
||||
[[nodiscard]] PixelFormat UnswizzleViewFormat(u32 bytes_per_block) {
|
||||
switch (bytes_per_block) {
|
||||
case 1:
|
||||
return PixelFormat::R8_UINT;
|
||||
case 2:
|
||||
return PixelFormat::R16_UINT;
|
||||
case 4:
|
||||
return PixelFormat::R32_UINT;
|
||||
case 8:
|
||||
return PixelFormat::R32G32_UINT;
|
||||
case 16:
|
||||
return PixelFormat::R32G32B32A32_UINT;
|
||||
default:
|
||||
return PixelFormat::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 UNSWIZZLE_WORKGROUP_INVOCATIONS = 32 * 32;
|
||||
|
||||
[[nodiscard]] bool SupportsAcceleratedUnswizzleDevice(const Device& device) {
|
||||
return device.IsKhrImageFormatListSupported() &&
|
||||
device.GetMaxComputeWorkGroupInvocations() >= UNSWIZZLE_WORKGROUP_INVOCATIONS;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool SupportsAcceleratedUnswizzle(const Device& device, const ImageInfo& info) {
|
||||
if (!SupportsAcceleratedUnswizzleDevice(device)) {
|
||||
return false;
|
||||
}
|
||||
if (info.num_samples > 1) {
|
||||
return false;
|
||||
}
|
||||
if (info.type != ImageType::e2D && info.type != ImageType::e3D &&
|
||||
info.type != ImageType::Linear) {
|
||||
return false;
|
||||
}
|
||||
const PixelFormat view_format =
|
||||
UnswizzleViewFormat(VideoCore::Surface::BytesPerBlock(info.format));
|
||||
if (view_format == PixelFormat::Invalid) {
|
||||
return false;
|
||||
}
|
||||
if (!VideoCore::Surface::IsViewCompatible(info.format, view_format, false, true)) {
|
||||
return false;
|
||||
}
|
||||
const auto host_format =
|
||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, view_format);
|
||||
return device.IsFormatSupported(host_format.format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT,
|
||||
FormatType::Optimal);
|
||||
}
|
||||
|
||||
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info,
|
||||
std::optional<VkFormat> format_override = {}) {
|
||||
auto format_info =
|
||||
@@ -248,8 +297,18 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
return allocator.CreateImage(image_ci);
|
||||
}
|
||||
|
||||
[[nodiscard]] VkImageViewType StorageViewType(ImageType type) {
|
||||
if (type == ImageType::e3D) {
|
||||
return VK_IMAGE_VIEW_TYPE_3D;
|
||||
}
|
||||
if (type == ImageType::Linear) {
|
||||
return VK_IMAGE_VIEW_TYPE_2D;
|
||||
}
|
||||
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image,
|
||||
VkFormat format) {
|
||||
VkFormat format, VkImageViewType view_type) {
|
||||
static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -260,7 +319,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
.pNext = &storage_image_view_usage_create_info,
|
||||
.flags = 0,
|
||||
.image = image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
|
||||
.viewType = view_type,
|
||||
.format = format,
|
||||
.components{
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
@@ -658,18 +717,11 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
|
||||
.subresourceRange = subresource_range,
|
||||
};
|
||||
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||
read_barrier);
|
||||
cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies);
|
||||
// TODO: Move this to another API
|
||||
cmdbuf.PipelineBarrier(
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
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);
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0,
|
||||
nullptr, nullptr, write_barrier);
|
||||
}
|
||||
|
||||
[[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region,
|
||||
@@ -968,6 +1020,14 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
|
||||
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
|
||||
staging_buffer_pool, compute_pass_descriptor_queue);
|
||||
}
|
||||
if (SupportsAcceleratedUnswizzleDevice(device)) {
|
||||
bl_unswizzle_2d_pass.emplace(device, scheduler, descriptor_pool,
|
||||
compute_pass_descriptor_queue);
|
||||
bl_unswizzle_image_3d_pass.emplace(device, scheduler, descriptor_pool,
|
||||
compute_pass_descriptor_queue);
|
||||
pitch_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
|
||||
compute_pass_descriptor_queue);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureCacheRuntime::Finish() {
|
||||
@@ -1902,16 +1962,12 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
|
||||
if (runtime->device.HasDebuggingToolAttached()) {
|
||||
original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
|
||||
}
|
||||
if (False(flags & VideoCommon::ImageFlagBits::Converted) &&
|
||||
SupportsAcceleratedUnswizzle(runtime->device, info)) {
|
||||
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
|
||||
}
|
||||
current_image = &Image::original_image;
|
||||
storage_image_views.resize(info.resources.levels);
|
||||
if (WillUseAcceleratedAstcDecode(runtime->device, info)) {
|
||||
const auto& device = runtime->device.GetLogical();
|
||||
const VkFormat storage_format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
for (s32 level = 0; level < info.resources.levels; ++level) {
|
||||
storage_image_views[level] =
|
||||
MakeStorageView(device, level, *original_image, storage_format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBase{params} {}
|
||||
@@ -2035,7 +2091,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
|
||||
temp_vk_image, info.format, info.num_samples,
|
||||
{image_copies.data(), image_copies.size()}, false);
|
||||
}
|
||||
initialized = true;
|
||||
InitializationFor(current_image) = true;
|
||||
runtime->ReleaseMsaaScratchImage(temp_vk_image);
|
||||
|
||||
if (is_rescaled) {
|
||||
@@ -2056,7 +2112,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
|
||||
const VkBuffer src_buffer = buffer;
|
||||
const VkImage vk_image = *original_image;
|
||||
const VkImageAspectFlags vk_aspect_mask = aspect_mask;
|
||||
const bool was_initialized = std::exchange(initialized, true);
|
||||
const bool was_initialized = std::exchange(InitializationFor(&Image::original_image), true);
|
||||
|
||||
scheduler->Record([src_buffer, vk_image, vk_aspect_mask, was_initialized,
|
||||
vk_copies](vk::CommandBuffer cmdbuf) {
|
||||
@@ -2321,16 +2377,46 @@ void Image::DownloadMemory(const StagingBufferRef& map, std::span<const BufferIm
|
||||
DownloadMemory(buffers, offsets, copies);
|
||||
}
|
||||
|
||||
std::vector<vk::ImageView>& Image::StorageViewsFor(vk::Image Image::*image) {
|
||||
if (image == &Image::scaled_image) {
|
||||
if (scaled_storage_image_views.empty()) {
|
||||
scaled_storage_image_views.resize(info.resources.levels);
|
||||
}
|
||||
return scaled_storage_image_views;
|
||||
}
|
||||
return storage_image_views;
|
||||
}
|
||||
|
||||
bool& Image::InitializationFor(vk::Image Image::*image) noexcept {
|
||||
if (image == &Image::scaled_image) {
|
||||
return scaled_initialized;
|
||||
}
|
||||
return original_initialized;
|
||||
}
|
||||
|
||||
VkImageView Image::StorageImageView(s32 level) noexcept {
|
||||
auto& view = storage_image_views[level];
|
||||
const bool astc_decode = WillUseAcceleratedAstcDecode(runtime->device, info);
|
||||
const bool unswizzle_upload =
|
||||
!astc_decode && True(flags & ImageFlagBits::AcceleratedUpload);
|
||||
vk::Image Image::*target = current_image;
|
||||
if (astc_decode || unswizzle_upload) {
|
||||
target = &Image::original_image;
|
||||
}
|
||||
auto& view = StorageViewsFor(target)[level];
|
||||
if (!view) {
|
||||
auto format_info =
|
||||
MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format);
|
||||
if (WillUseAcceleratedAstcDecode(runtime->device, info)) {
|
||||
if (astc_decode) {
|
||||
format_info.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
}
|
||||
view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image),
|
||||
format_info.format);
|
||||
if (unswizzle_upload) {
|
||||
const PixelFormat view_format =
|
||||
UnswizzleViewFormat(VideoCore::Surface::BytesPerBlock(info.format));
|
||||
format_info = MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, false,
|
||||
view_format);
|
||||
}
|
||||
view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*target),
|
||||
format_info.format, StorageViewType(info.type));
|
||||
}
|
||||
return *view;
|
||||
}
|
||||
@@ -2370,6 +2456,7 @@ bool Image::ScaleUp(bool ignore) {
|
||||
}
|
||||
if (NeedsScaleHelper()) {
|
||||
if (!BlitScaleHelper(true)) {
|
||||
flags &= ~ImageFlagBits::Rescaled;
|
||||
current_image = &Image::original_image;
|
||||
return false;
|
||||
}
|
||||
@@ -2559,6 +2646,10 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||
if (device->IsExtAstcDecodeModeSupported() && IsLdrAstcFormat(format_info.format)) {
|
||||
view_next = &astc_decode_mode;
|
||||
}
|
||||
auto subresource_range = MakeSubresourceRange(aspect_mask, info.range);
|
||||
if (True(flags & VideoCommon::ImageViewFlagBits::Slice)) {
|
||||
subresource_range.levelCount = 1;
|
||||
}
|
||||
const VkImageViewCreateInfo create_info{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = view_next,
|
||||
@@ -2567,7 +2658,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||
.viewType = VkImageViewType{},
|
||||
.format = format_info.format,
|
||||
.components = swizzle_mapping,
|
||||
.subresourceRange = MakeSubresourceRange(aspect_mask, info.range),
|
||||
.subresourceRange = subresource_range,
|
||||
};
|
||||
const auto create = [&](TextureType tex_type, std::optional<u32> num_layers) {
|
||||
VkImageViewCreateInfo ci{create_info};
|
||||
@@ -3141,6 +3232,19 @@ void TextureCacheRuntime::AccelerateImageUpload(
|
||||
return astc_decoder_pass->Assemble(image, map, swizzles);
|
||||
}
|
||||
|
||||
if (bl_unswizzle_2d_pass && image.info.type == ImageType::e2D) {
|
||||
return bl_unswizzle_2d_pass->Unswizzle(image, map, swizzles);
|
||||
}
|
||||
|
||||
if (bl_unswizzle_image_3d_pass && image.info.type == ImageType::e3D &&
|
||||
!IsPixelFormatBCn(image.info.format)) {
|
||||
return bl_unswizzle_image_3d_pass->Unswizzle(image, map, swizzles);
|
||||
}
|
||||
|
||||
if (pitch_unswizzle_pass && image.info.type == ImageType::Linear) {
|
||||
return pitch_unswizzle_pass->Unswizzle(image, map, swizzles);
|
||||
}
|
||||
|
||||
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
|
||||
if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) {
|
||||
ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
|
||||
|
||||
@@ -159,6 +159,9 @@ public:
|
||||
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
||||
|
||||
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
|
||||
std::optional<BlockLinearUnswizzle2DPass> bl_unswizzle_2d_pass;
|
||||
std::optional<BlockLinearUnswizzleImage3DPass> bl_unswizzle_image_3d_pass;
|
||||
std::optional<PitchUnswizzlePass> pitch_unswizzle_pass;
|
||||
const Settings::ResolutionScalingInfo& resolution;
|
||||
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
|
||||
|
||||
@@ -350,7 +353,7 @@ public:
|
||||
|
||||
/// Returns true when the image is already initialized and mark it as initialized
|
||||
[[nodiscard]] bool ExchangeInitialization() noexcept {
|
||||
return std::exchange(initialized, true);
|
||||
return std::exchange(InitializationFor(current_image), true);
|
||||
}
|
||||
|
||||
VkImageView StorageImageView(s32 level) noexcept;
|
||||
@@ -370,6 +373,10 @@ private:
|
||||
|
||||
bool NeedsScaleHelper() const;
|
||||
|
||||
std::vector<vk::ImageView>& StorageViewsFor(vk::Image Image::*image);
|
||||
|
||||
bool& InitializationFor(vk::Image Image::*image) noexcept;
|
||||
|
||||
Scheduler* scheduler{};
|
||||
TextureCacheRuntime* runtime{};
|
||||
|
||||
@@ -387,8 +394,10 @@ private:
|
||||
vk::Image Image::*current_image{};
|
||||
|
||||
std::vector<vk::ImageView> storage_image_views;
|
||||
std::vector<vk::ImageView> scaled_storage_image_views;
|
||||
VkImageAspectFlags aspect_mask = 0;
|
||||
bool initialized = false;
|
||||
bool original_initialized = false;
|
||||
bool scaled_initialized = false;
|
||||
|
||||
std::optional<Framebuffer> scale_framebuffer;
|
||||
std::optional<Framebuffer> normal_framebuffer;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -23,8 +26,8 @@ struct BlockLinearSwizzle2DParams {
|
||||
};
|
||||
|
||||
struct BlockLinearSwizzle3DParams {
|
||||
std::array<u32, 3> origin;
|
||||
std::array<s32, 3> destination;
|
||||
alignas(16) std::array<u32, 3> origin;
|
||||
alignas(16) std::array<s32, 3> destination;
|
||||
u32 bytes_per_block_log2;
|
||||
u32 slice_size;
|
||||
u32 block_size;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
#include "video_core/guest_memory.h"
|
||||
#include "video_core/host1x/gpu_device_memory_manager.h"
|
||||
#include "video_core/texture_cache/accelerated_swizzle.h"
|
||||
#include "video_core/texture_cache/image_view_base.h"
|
||||
#include "video_core/texture_cache/samples_helper.h"
|
||||
#include "video_core/texture_cache/texture_cache_base.h"
|
||||
@@ -279,11 +280,11 @@ void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) {
|
||||
|
||||
const ImageId view_image_id = slot_image_views[view.id].image_id;
|
||||
{
|
||||
bool is_continue = false;
|
||||
bool is_feedback = false;
|
||||
for (size_t i = 0; i < 8; ++i)
|
||||
is_continue |= (rt_active_mask & (1u << i)) && view_image_id == rt_image_id[i];
|
||||
if (is_continue)
|
||||
continue;
|
||||
is_feedback |= (rt_active_mask & (1u << i)) && view_image_id == rt_image_id[i];
|
||||
if (is_feedback)
|
||||
return true;
|
||||
}
|
||||
if (depth_active && view_image_id == rt_depth_image_id) {
|
||||
return true;
|
||||
@@ -626,14 +627,25 @@ void TextureCache<P>::DownloadMemory(DAddr cpu_addr, size_t size) {
|
||||
std::ranges::sort(images, [this](ImageId lhs, ImageId rhs) {
|
||||
return slot_images[lhs].modification_tick < slot_images[rhs].modification_tick;
|
||||
});
|
||||
size_t total_size_bytes = 0;
|
||||
for (const ImageId image_id : images) {
|
||||
total_size_bytes += slot_images[image_id].unswizzled_size_bytes;
|
||||
}
|
||||
auto download_map = runtime.DownloadStagingBuffer(total_size_bytes);
|
||||
for (const ImageId image_id : images) {
|
||||
Image& image = slot_images[image_id];
|
||||
auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes);
|
||||
const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info));
|
||||
image.DownloadMemory(map, copies);
|
||||
runtime.Finish();
|
||||
SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span,
|
||||
image.DownloadMemory(download_map, copies);
|
||||
download_map.offset += image.unswizzled_size_bytes;
|
||||
}
|
||||
runtime.Finish();
|
||||
std::span<u8> download_span = download_map.mapped_span;
|
||||
for (const ImageId image_id : images) {
|
||||
const ImageBase& image = slot_images[image_id];
|
||||
const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info));
|
||||
SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span,
|
||||
swizzle_data_buffer);
|
||||
download_span = download_span.subspan(image.unswizzled_size_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1122,6 +1134,12 @@ void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
|
||||
|
||||
TrackImage(image, image_id);
|
||||
|
||||
if (image.info.rescaleable &&
|
||||
IsRegionGpuModified(image.cpu_addr, image.guest_size_bytes)) {
|
||||
runtime.TransitionImageLayout(image);
|
||||
return;
|
||||
}
|
||||
|
||||
if (image.info.num_samples > 1 && !runtime.CanUploadMSAA()) {
|
||||
LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented");
|
||||
runtime.TransitionImageLayout(image);
|
||||
@@ -1157,8 +1175,7 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
|
||||
const GPUVAddr gpu_addr = image.gpu_addr;
|
||||
|
||||
if (True(image.flags & ImageFlagBits::AcceleratedUpload)) {
|
||||
gpu_memory->ReadBlock(gpu_addr, mapped_span.data(), mapped_span.size_bytes(),
|
||||
VideoCommon::CacheType::NoTextureCache);
|
||||
gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), image.guest_size_bytes);
|
||||
const auto uploads = FullUploadSwizzles(image.info);
|
||||
runtime.AccelerateImageUpload(image, staging, FixSmallVectorADL(uploads), 0, 0);
|
||||
return;
|
||||
@@ -1265,6 +1282,9 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
|
||||
|
||||
template <class P>
|
||||
bool TextureCache<P>::ImageCanRescale(ImageBase& image) {
|
||||
if (!Settings::values.resolution_info.active) {
|
||||
return false;
|
||||
}
|
||||
if (!image.info.rescaleable) {
|
||||
return false;
|
||||
}
|
||||
@@ -1352,12 +1372,12 @@ void TextureCache<P>::QueueAsyncDecode(Image& image, ImageId image_id) {
|
||||
decode->image_id = image_id;
|
||||
async_decodes.push_back(std::move(decode));
|
||||
|
||||
std::vector<u8> local_unswizzle_data_buffer(image.unswizzled_size_bytes, 0);
|
||||
Common::ScratchBuffer<u8> local_unswizzle_data_buffer(image.unswizzled_size_bytes);
|
||||
Tegra::Memory::GpuGuestMemory<u8, Tegra::Memory::GuestMemoryFlags::UnsafeRead> swizzle_data(*gpu_memory, image.gpu_addr, image.guest_size_bytes, &swizzle_data_buffer);
|
||||
auto copies = UnswizzleImage(*gpu_memory, image.gpu_addr, image.info, swizzle_data, local_unswizzle_data_buffer);
|
||||
const size_t out_size = MapSizeBytes(image);
|
||||
|
||||
auto func = [out_size, copies, info = image.info,
|
||||
auto func = [out_size, copies = std::move(copies), info = image.info,
|
||||
input = std::move(local_unswizzle_data_buffer),
|
||||
async_decode = decode_ptr]() mutable {
|
||||
async_decode->decoded_data.resize_destructive(out_size);
|
||||
@@ -1426,18 +1446,16 @@ void TextureCache<P>::TickAsyncUnswizzle() {
|
||||
Image& image = slot_images[task.image_id];
|
||||
|
||||
if (!task.initialized) {
|
||||
task.total_size = MapSizeBytes(image);
|
||||
task.total_size = image.guest_size_bytes;
|
||||
task.staging_buffer = runtime.UploadStagingBuffer(task.total_size, true);
|
||||
|
||||
const auto& info = image.info;
|
||||
const u32 bytes_per_block = BytesPerBlock(info.format);
|
||||
const u32 width_blocks = Common::DivCeil(info.size.width, 4u);
|
||||
const u32 height_blocks = Common::DivCeil(info.size.height, 4u);
|
||||
|
||||
const u32 stride = width_blocks * bytes_per_block;
|
||||
const u32 aligned_height = height_blocks;
|
||||
task.bytes_per_slice = static_cast<size_t>(stride) * aligned_height;
|
||||
const auto layout = FullUploadSwizzles(task.info);
|
||||
const auto params =
|
||||
VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams(layout.front(), task.info);
|
||||
task.bytes_per_slice = params.slice_size;
|
||||
task.chunked = task.info.block.depth == 0;
|
||||
task.last_submitted_offset = 0;
|
||||
task.slices_submitted = 0;
|
||||
task.initialized = true;
|
||||
}
|
||||
|
||||
@@ -1452,31 +1470,39 @@ void TextureCache<P>::TickAsyncUnswizzle() {
|
||||
if (copy_amount == 0) copy_amount = task.bytes_per_slice;
|
||||
}
|
||||
|
||||
gpu_memory->ReadBlock(image.gpu_addr + task.current_offset,
|
||||
task.staging_buffer.mapped_span.data() + task.current_offset,
|
||||
copy_amount);
|
||||
gpu_memory->ReadBlockUnsafe(image.gpu_addr + task.current_offset,
|
||||
task.staging_buffer.mapped_span.data() + task.current_offset,
|
||||
copy_amount);
|
||||
task.current_offset += copy_amount;
|
||||
}
|
||||
|
||||
const bool is_final_batch = task.current_offset >= task.total_size;
|
||||
const size_t bytes_ready = task.current_offset - task.last_submitted_offset;
|
||||
const u32 complete_slices = static_cast<u32>(bytes_ready / task.bytes_per_slice);
|
||||
|
||||
if (complete_slices >= swizzle_slices_per_batch || (is_final_batch && complete_slices > 0)) {
|
||||
const u32 z_start = static_cast<u32>(task.last_submitted_offset / task.bytes_per_slice);
|
||||
const u32 slices_to_process = (std::min)(complete_slices, swizzle_slices_per_batch);
|
||||
const u32 z_count = (std::min)(slices_to_process, image.info.size.depth - z_start);
|
||||
if (task.chunked) {
|
||||
const size_t bytes_ready = task.current_offset - task.last_submitted_offset;
|
||||
const u32 complete_slices = static_cast<u32>(bytes_ready / task.bytes_per_slice);
|
||||
|
||||
if (z_count > 0) {
|
||||
const auto uploads = FullUploadSwizzles(task.info);
|
||||
runtime.AccelerateImageUpload(image, task.staging_buffer, FixSmallVectorADL(uploads), z_start, z_count);
|
||||
task.last_submitted_offset += (static_cast<size_t>(z_count) * task.bytes_per_slice);
|
||||
if (complete_slices >= swizzle_slices_per_batch || (is_final_batch && complete_slices > 0)) {
|
||||
const u32 z_start = task.slices_submitted;
|
||||
const u32 slices_to_process = (std::min)(complete_slices, swizzle_slices_per_batch);
|
||||
const u32 z_count = (std::min)(slices_to_process, image.info.size.depth - z_start);
|
||||
|
||||
if (z_count > 0) {
|
||||
const auto uploads = FullUploadSwizzles(task.info);
|
||||
runtime.AccelerateImageUpload(image, task.staging_buffer,
|
||||
FixSmallVectorADL(uploads), z_start, z_count);
|
||||
task.last_submitted_offset += static_cast<size_t>(z_count) * task.bytes_per_slice;
|
||||
task.slices_submitted += z_count;
|
||||
}
|
||||
}
|
||||
} else if (is_final_batch && task.slices_submitted == 0) {
|
||||
const auto uploads = FullUploadSwizzles(task.info);
|
||||
runtime.AccelerateImageUpload(image, task.staging_buffer, FixSmallVectorADL(uploads), 0,
|
||||
image.info.size.depth);
|
||||
task.slices_submitted = image.info.size.depth;
|
||||
}
|
||||
|
||||
// Check if complete
|
||||
const u32 slices_submitted = static_cast<u32>(task.last_submitted_offset / task.bytes_per_slice);
|
||||
const bool all_slices_submitted = slices_submitted >= image.info.size.depth;
|
||||
const bool all_slices_submitted = task.slices_submitted >= image.info.size.depth;
|
||||
|
||||
if (is_final_batch && all_slices_submitted) {
|
||||
runtime.FreeDeferredStagingBuffer(task.staging_buffer);
|
||||
|
||||
@@ -138,6 +138,8 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI
|
||||
AsyncBuffer staging_buffer;
|
||||
size_t last_submitted_offset = 0;
|
||||
size_t bytes_per_slice;
|
||||
u32 slices_submitted = 0;
|
||||
bool chunked = false;
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -1118,6 +1118,11 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
|
||||
SetNext(next, properties.transform_feedback);
|
||||
}
|
||||
if (extensions.vertex_attribute_divisor) {
|
||||
properties.vertex_attribute_divisor.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT;
|
||||
SetNext(next, properties.vertex_attribute_divisor);
|
||||
}
|
||||
if (extensions.maintenance5) {
|
||||
properties.maintenance5.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR;
|
||||
|
||||
@@ -70,6 +70,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
||||
FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \
|
||||
FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \
|
||||
FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \
|
||||
FEATURE(EXT, VertexAttributeDivisor, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
|
||||
FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \
|
||||
FEATURE(KHR, Maintenance5, MAINTENANCE_5, maintenance5) \
|
||||
FEATURE(KHR, Maintenance6, MAINTENANCE_6, maintenance6) \
|
||||
@@ -91,7 +92,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
||||
EXTENSION(EXT, SHADER_STENCIL_EXPORT, shader_stencil_export) \
|
||||
EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \
|
||||
EXTENSION(EXT, TOOLING_INFO, tooling_info) \
|
||||
EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
|
||||
EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \
|
||||
EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \
|
||||
EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \
|
||||
@@ -354,6 +354,7 @@ public:
|
||||
|
||||
#define FN_MAX_LIMIT_LIST \
|
||||
FN_MAX_LIMIT_ELEM(ComputeSharedMemorySize) \
|
||||
FN_MAX_LIMIT_ELEM(ComputeWorkGroupInvocations) \
|
||||
FN_MAX_LIMIT_ELEM(PerStageDescriptorSampledImages) \
|
||||
FN_MAX_LIMIT_ELEM(PerStageResources) \
|
||||
FN_MAX_LIMIT_ELEM(DescriptorSetSamplers) \
|
||||
@@ -679,6 +680,32 @@ FN_MAX_LIMIT_LIST
|
||||
return features.host_query_reset.hostQueryReset != VK_FALSE;
|
||||
}
|
||||
|
||||
u32 GetMaxVertexAttribDivisor() const {
|
||||
const u32 reported = properties.vertex_attribute_divisor.maxVertexAttribDivisor;
|
||||
if (reported == 0) {
|
||||
return 1;
|
||||
}
|
||||
return reported;
|
||||
}
|
||||
|
||||
bool IsVertexAttributeInstanceRateZeroDivisorSupported() const {
|
||||
return features.vertex_attribute_divisor.vertexAttributeInstanceRateZeroDivisor == VK_TRUE;
|
||||
}
|
||||
|
||||
u32 GetVertexAttribDivisor(u32 frequency) const {
|
||||
const u32 max_divisor = GetMaxVertexAttribDivisor();
|
||||
if (frequency == 0) {
|
||||
if (IsVertexAttributeInstanceRateZeroDivisorSupported()) {
|
||||
return 0;
|
||||
}
|
||||
return max_divisor;
|
||||
}
|
||||
if (frequency > max_divisor) {
|
||||
return max_divisor;
|
||||
}
|
||||
return frequency;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_transform_feedback.
|
||||
bool IsExtTransformFeedbackSupported() const {
|
||||
return extensions.transform_feedback;
|
||||
@@ -1174,6 +1201,7 @@ private:
|
||||
VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer{};
|
||||
VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
|
||||
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
|
||||
VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT vertex_attribute_divisor{};
|
||||
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
|
||||
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
|
||||
VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color{};
|
||||
|
||||
Reference in New Issue
Block a user