diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index d7eb21612f..a5d7774dc3 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h @@ -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; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index e40aad1fb5..b23e2ec915 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include @@ -121,25 +122,6 @@ void BufferCache

::WriteMemory(DAddr device_addr, u64 size) { memory_tracker.MarkRegionAsCpuModified(device_addr, size); } -template -void BufferCache

::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 bool BufferCache

::OnCPUWrite(DAddr device_addr, u64 size) { const bool is_dirty = IsRegionRegistered(device_addr, size); @@ -422,7 +404,7 @@ void BufferCache

::UnbindGraphicsStorageBuffers(size_t stage) { } template -bool BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, +void BufferCache

::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

::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

::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 @@ -762,16 +743,6 @@ void BufferCache

::BindHostIndexBuffer() { } } -template -void BufferCache

::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 Binding& BufferCache

::VertexBufferSlot(u32 index) { ASSERT(index < NUM_VERTEX_BUFFERS); @@ -1197,7 +1168,7 @@ void BufferCache

::DoUpdateGraphicsBuffers(bool is_indexed) { if (is_indexed) { UpdateIndexBuffer(); } - UpdateVertexBuffers(); + UpdateVertexBuffers(is_indexed); UpdateTransformFeedbackBuffers(); for (size_t stage = 0; stage < NUM_STAGES; ++stage) { UpdateUniformBuffers(stage); @@ -1251,9 +1222,14 @@ void BufferCache

::UpdateIndexBuffer() { const GPUVAddr gpu_addr_begin = index_buffer_ref.StartAddress(); const GPUVAddr gpu_addr_end = index_buffer_ref.EndAddress(); const std::optional device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin); - const u32 address_size = static_cast(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::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((std::min)(address_size, draw_size)); if (size == 0 || !device_addr) { channel_state->index_buffer = NULL_BINDING; return; @@ -1266,20 +1242,142 @@ void BufferCache

::UpdateIndexBuffer() { } template -void BufferCache

::UpdateVertexBuffers() { +u64 BufferCache

::DrawMaxIndex() { + if (max_index_scanned) { + return cached_max_index; + } + max_index_scanned = true; + cached_max_index = 0; + const auto& index_buffer_ref = maxwell3d->draw_manager.draw_state.index_buffer; + const u32 count = index_buffer_ref.count; + if (count == 0) { + return 0; + } + index_scan_buffer.resize_destructive(count); + gpu_memory->ReadBlockUnsafe(index_buffer_ref.IndexStart(), index_scan_buffer.data(), + size_t{count} * sizeof(u32)); + u32 restart_index = (std::numeric_limits::max)(); + if (maxwell3d->regs.primitive_restart.enabled != 0) { + restart_index = maxwell3d->regs.primitive_restart.index; + } + u32 max_index = 0; + for (u32 i = 0; i < count; ++i) { + const u32 value = index_scan_buffer[i]; + if (value == restart_index) { + continue; + } + max_index = (std::max)(max_index, value); + } + cached_max_index = max_index; + return cached_max_index; +} + +template +u64 BufferCache

::StreamAttributeExtent(u32 index) { + using VertexAttribute = typename Maxwell::VertexAttribute; + if (stream_extents_valid) { + return stream_extents[index]; + } + stream_extents_valid = true; + stream_extents.fill(0); + for (size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { + const auto& attribute = maxwell3d->regs.vertex_attrib_format[i]; + if (attribute.constant != 0 || attribute.size == VertexAttribute::Size::Invalid) { + continue; + } + const u32 buffer = attribute.buffer.Value(); + if (buffer >= NUM_VERTEX_BUFFERS) { + continue; + } + const u64 end = static_cast(attribute.offset.Value()) + + static_cast(attribute.SizeInBytes()); + stream_extents[buffer] = (std::max)(stream_extents[buffer], end); + } + return stream_extents[index]; +} + +template +u64 BufferCache

::DrawVertexBound(u32 index, bool is_indexed) { + const auto& array = maxwell3d->regs.vertex_streams[index]; + if (array.enable == 0) { + return 0; + } + const u64 extent = StreamAttributeExtent(index); + if (extent == 0) { + return 0; + } + const u64 stride = static_cast(array.stride); + if (stride == 0) { + return extent; + } + const auto& draw_state = maxwell3d->draw_manager.draw_state; + u64 elements = 0; + if (maxwell3d->regs.vertex_stream_instances.IsInstancingEnabled(index)) { + if (draw_instance_count == 0) { + return 0; + } + const u64 base_instance = static_cast(draw_state.base_instance); + elements = base_instance + 1; + if (array.frequency != 0) { + elements = (base_instance + static_cast(draw_instance_count) - 1) / + static_cast(array.frequency) + + 1; + } + } else if (!is_indexed) { + elements = static_cast(draw_state.vertex_buffer.first) + + static_cast(draw_state.vertex_buffer.count); + } else { + const auto format = draw_state.index_buffer.format; + u64 max_index = 0xFF; + if (format == Maxwell::IndexFormat::UnsignedShort) { + max_index = 0xFFFF; + } else if (format != Maxwell::IndexFormat::UnsignedByte) { + const auto& limit = maxwell3d->regs.vertex_stream_limits[index]; + const GPUVAddr gpu_addr_begin = array.Address(); + const GPUVAddr gpu_addr_end = limit.Address() + 1; + if (gpu_addr_end <= gpu_addr_begin) { + return 0; + } + const bool walks = gpu_addr_end - gpu_addr_begin >= IMPLAUSIBLE_VERTEX_SIZE || + !gpu_memory->IsWithinGPUAddressRange(gpu_addr_end); + if (!walks) { + return 0; + } + max_index = DrawMaxIndex(); + } + elements = static_cast(draw_state.base_index) + max_index + 1; + } + if (elements == 0) { + return extent; + } + return (elements - 1) * stride + extent; +} + +template +void BufferCache

::UpdateVertexBuffers(bool is_indexed) { auto& flags = maxwell3d->dirty.flags; + max_index_scanned = false; + stream_extents_valid = false; + for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { + const u64 bound = DrawVertexBound(index, is_indexed); + if (bound <= last_draw_bounds[index]) { + continue; + } + flags[Dirty::VertexBuffer0 + index] = true; + flags[Dirty::VertexBuffers] = true; + } if (!maxwell3d->dirty.flags[Dirty::VertexBuffers]) { return; } flags[Dirty::VertexBuffers] = false; for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { - UpdateVertexBuffer(index); + UpdateVertexBuffer(index, is_indexed); } } template -void BufferCache

::UpdateVertexBuffer(u32 index) { +void BufferCache

::UpdateVertexBuffer(u32 index, bool is_indexed) { if (!maxwell3d->dirty.flags[Dirty::VertexBuffer0 + index]) { return; } @@ -1288,15 +1386,31 @@ void BufferCache

::UpdateVertexBuffer(u32 index) { const GPUVAddr gpu_addr_begin = array.Address(); const GPUVAddr gpu_addr_end = limit.Address() + 1; const std::optional device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin); - const u32 address_size = static_cast(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(gpu_memory->MaxContinuousRange(gpu_addr_begin, size)); + // TODO: Analyze stride and number of vertices + constexpr u64 implausible_size = IMPLAUSIBLE_VERTEX_SIZE; + u64 address_size = gpu_addr_end - gpu_addr_begin; + if (address_size > u64{(std::numeric_limits::max)()}) { + address_size = implausible_size; + } + const u64 draw_bound = DrawVertexBound(index, is_indexed); + last_draw_bounds[index] = (std::numeric_limits::max)(); + if (draw_bound != 0) { + last_draw_bounds[index] = draw_bound; + address_size = (std::min)(address_size, draw_bound); + } + 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(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 +1692,10 @@ template BufferId BufferCache

::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(device_addr_end - device_addr); + constexpr u64 max_buffer_size = u64{(std::numeric_limits::max)()}; + wanted_size = static_cast((std::min)(device_addr_end - device_addr, max_buffer_size)); const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size); - const u32 size = static_cast(overlap.end - overlap.begin); + const u32 size = static_cast((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(); diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 0a521192c6..4dba335cc8 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -51,6 +51,7 @@ constexpr u32 NUM_VERTEX_BUFFERS = 16; #else constexpr u32 NUM_VERTEX_BUFFERS = 32; #endif +constexpr u64 IMPLAUSIBLE_VERTEX_SIZE = 64_MiB; constexpr u32 NUM_TRANSFORM_FEEDBACK_BUFFERS = 4; constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18; constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8; @@ -217,8 +218,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 +247,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 +308,10 @@ public: current_draw_indirect = current_draw_indirect_; } + void SetDrawInstanceCount(u32 draw_instance_count_) { + draw_instance_count = draw_instance_count_; + } + [[nodiscard]] std::pair GetDrawIndirectCount(); [[nodiscard]] std::pair GetDrawIndirectBuffer(); @@ -376,8 +379,6 @@ private: void BindHostTransformFeedbackBuffers(); - void BindHostVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride); - void BindHostComputeUniformBuffers(); void BindHostComputeStorageBuffers(); @@ -390,9 +391,15 @@ private: void UpdateIndexBuffer(); - void UpdateVertexBuffers(); + void UpdateVertexBuffers(bool is_indexed); - void UpdateVertexBuffer(u32 index); + void UpdateVertexBuffer(u32 index, bool is_indexed); + + [[nodiscard]] u64 DrawVertexBound(u32 index, bool is_indexed); + + [[nodiscard]] u64 DrawMaxIndex(); + + [[nodiscard]] u64 StreamAttributeExtent(u32 index); void UpdateDrawIndirect(); @@ -484,6 +491,14 @@ private: const Tegra::Engines::Maxwell3D::DrawManager::IndirectParams* current_draw_indirect{}; + u32 draw_instance_count = 0; + std::array last_draw_bounds{}; + Common::ScratchBuffer index_scan_buffer; + u64 cached_max_index = 0; + bool max_index_scanned = false; + std::array stream_extents{}; + bool stream_extents_valid = false; + u32 last_index_count = 0; u32 enabled_vertex_buffers_mask = 0; diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index c4e7ac3f6f..ed48ed7973 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -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}) diff --git a/src/video_core/host_shaders/block_linear_unswizzle_2d.comp b/src/video_core/host_shaders/block_linear_unswizzle_2d.comp index 2a3446ca66..df4bf16136 100644 --- a/src/video_core/host_shaders/block_linear_unswizzle_2d.comp +++ b/src/video_core/host_shaders/block_linear_unswizzle_2d.comp @@ -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) diff --git a/src/video_core/host_shaders/block_linear_unswizzle_3d.comp b/src/video_core/host_shaders/block_linear_unswizzle_3d.comp index 34e4f93307..f89ed10858 100644 --- a/src/video_core/host_shaders/block_linear_unswizzle_3d.comp +++ b/src/video_core/host_shaders/block_linear_unswizzle_3d.comp @@ -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) diff --git a/src/video_core/host_shaders/pitch_unswizzle.comp b/src/video_core/host_shaders/pitch_unswizzle.comp index 7d23f594c7..8f07704fa9 100644 --- a/src/video_core/host_shaders/pitch_unswizzle.comp +++ b/src/video_core/host_shaders/pitch_unswizzle.comp @@ -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) diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index daff57bd00..ada65ae756 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -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(stride)); - glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, index, - buffer.HostGpuAddr() + offset, static_cast(size)); - } else { - glBindVertexBuffer(index, buffer.Handle(), static_cast(offset), - static_cast(stride)); - } -} - void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) { // TODO: Should HostBindings provide the correct runtime types to avoid these transforms? std::array buffer_handles; diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 09fa3a59af..e23dc33f74 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -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& bindings); void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 1d77d28c46..0e2dc63a56 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -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; diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index a734bd049c..168083d23c 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -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& bindings) { boost::container::static_vector buffer_handles(bindings.buffers.size()); for (u32 i = 0; i < bindings.buffers.size(); ++i) { diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 9b3dd50eaa..d23ebe2357 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -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& bindings); void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size); diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index ab58560170..adc1d1f2e3 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -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 + 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 + 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 UnswizzleSpv(const Device& device, + std::span extended, + std::span narrow) { + if (device.IsStorageBuffer8BitAccessSupported() && + device.IsStorageBuffer16BitAccessSupported()) { + return extended; + } + return narrow; +} + +struct PitchUnswizzlePushConstants { + alignas(8) std::array origin; + alignas(8) std::array 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, + 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 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(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, + 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 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, + 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 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 diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h index 3d30aa6bdc..519ca078ab 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -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 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 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 swizzles); + +private: + Scheduler& scheduler; + ComputePassDescriptorQueue& compute_pass_descriptor_queue; +}; + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 8b5b0bc9c5..b2c6cffa82 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -694,9 +694,11 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { const size_t num_vertex_arrays = (std::min)( Maxwell::NumVertexArrays, static_cast(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(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(index), - .divisor = key.state.binding_divisors[index], + .divisor = device.GetVertexAttribDivisor(key.state.binding_divisors[index]), }); } } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 3b0fea2fec..96099565a8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -137,10 +137,12 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3 max_y = (std::max)(max_y, 0); if (src.enable) { - scissor.offset.x = scale_up(src.min_x); + const s32 min_x = static_cast(src.min_x.Value()); + const s32 max_x = static_cast(src.max_x.Value()); + scissor.offset.x = scale_up(min_x); scissor.offset.y = scale_up(min_y); - scissor.extent.width = scale_up(src.max_x - src.min_x); - scissor.extent.height = scale_up(max_y - min_y); + scissor.extent.width = scale_up((std::max)(max_x - min_x, 0)); + scissor.extent.height = scale_up((std::max)(max_y - min_y, 0)); } else { scissor.offset.x = 0; scissor.offset.y = 0; @@ -260,6 +262,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 +298,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 +1921,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, }); } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 92dcd20111..33887f2f84 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -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{ diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index c3f56408b5..7b31c6e76d 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -160,6 +160,55 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& 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 format_override = {}) { auto format_info = @@ -248,8 +297,18 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& 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& 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& 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 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"); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 68bf2af991..29133d6bfb 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -159,6 +159,9 @@ public: std::optional astc_decoder_pass; std::optional bl3d_unswizzle_pass; + std::optional bl_unswizzle_2d_pass; + std::optional bl_unswizzle_image_3d_pass; + std::optional pitch_unswizzle_pass; const Settings::ResolutionScalingInfo& resolution; std::array, 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& 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 storage_image_views; + std::vector scaled_storage_image_views; VkImageAspectFlags aspect_mask = 0; - bool initialized = false; + bool original_initialized = false; + bool scaled_initialized = false; std::optional scale_framebuffer; std::optional normal_framebuffer; diff --git a/src/video_core/texture_cache/accelerated_swizzle.h b/src/video_core/texture_cache/accelerated_swizzle.h index d4250da689..97ce7b8a18 100644 --- a/src/video_core/texture_cache/accelerated_swizzle.h +++ b/src/video_core/texture_cache/accelerated_swizzle.h @@ -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 origin; - std::array destination; + alignas(16) std::array origin; + alignas(16) std::array destination; u32 bytes_per_block_log2; u32 slice_size; u32 block_size; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 640bd38407..821e0a0f56 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -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

::CheckFeedbackLoop(std::span 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

::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 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

::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

::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

::FindImage(const ImageInfo& info, GPUVAddr gpu_addr, template bool TextureCache

::ImageCanRescale(ImageBase& image) { + if (!Settings::values.resolution_info.active) { + return false; + } if (!image.info.rescaleable) { return false; } @@ -1352,12 +1372,12 @@ void TextureCache

::QueueAsyncDecode(Image& image, ImageId image_id) { decode->image_id = image_id; async_decodes.push_back(std::move(decode)); - std::vector local_unswizzle_data_buffer(image.unswizzled_size_bytes, 0); + Common::ScratchBuffer local_unswizzle_data_buffer(image.unswizzled_size_bytes); Tegra::Memory::GpuGuestMemory 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

::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(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

::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(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(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(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(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(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(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); diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 8a2318cc72..a5e5bd4918 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -139,6 +139,8 @@ class TextureCache : public VideoCommon::ChannelSetupCaches max_divisor) { + return max_divisor; + } + return frequency; + } + /// Returns true if the device supports VK_EXT_transform_feedback. bool IsExtTransformFeedbackSupported() const { return extensions.transform_feedback; @@ -1189,6 +1216,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{};