diff options
Diffstat (limited to 'src')
20 files changed, 830 insertions, 150 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 9b0c3db68..9afc6105d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -15,6 +15,10 @@ endif () if (DEFINED ENV{DISPLAYVERSION}) set(DISPLAY_VERSION $ENV{DISPLAYVERSION}) endif () + +# Pass the path to git to the GenerateSCMRev.cmake as well +find_package(Git QUIET) + add_custom_command(OUTPUT scm_rev.cpp COMMAND ${CMAKE_COMMAND} -DSRC_DIR="${CMAKE_SOURCE_DIR}" @@ -23,6 +27,7 @@ add_custom_command(OUTPUT scm_rev.cpp -DTITLE_BAR_FORMAT_RUNNING="${TITLE_BAR_FORMAT_RUNNING}" -DBUILD_TAG="${BUILD_TAG}" -DBUILD_ID="${DISPLAY_VERSION}" + -DGIT_EXECUTABLE="${GIT_EXECUTABLE}" -P "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake" DEPENDS # WARNING! It was too much work to try and make a common location for this list, diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp index f53a8d193..200c6489a 100644 --- a/src/common/telemetry.cpp +++ b/src/common/telemetry.cpp @@ -44,20 +44,6 @@ template class Field<std::string>; template class Field<const char*>; template class Field<std::chrono::microseconds>; -#ifdef ARCHITECTURE_x86_64 -static const char* CpuVendorToStr(Common::CPUVendor vendor) { - switch (vendor) { - case Common::CPUVendor::INTEL: - return "Intel"; - case Common::CPUVendor::AMD: - return "Amd"; - case Common::CPUVendor::OTHER: - return "Other"; - } - UNREACHABLE(); -} -#endif - void AppendBuildInfo(FieldCollection& fc) { const bool is_git_dirty{std::strstr(Common::g_scm_desc, "dirty") != nullptr}; fc.AddField(FieldType::App, "Git_IsDirty", is_git_dirty); @@ -71,7 +57,6 @@ void AppendCPUInfo(FieldCollection& fc) { #ifdef ARCHITECTURE_x86_64 fc.AddField(FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string); fc.AddField(FieldType::UserSystem, "CPU_BrandString", Common::GetCPUCaps().brand_string); - fc.AddField(FieldType::UserSystem, "CPU_Vendor", CpuVendorToStr(Common::GetCPUCaps().vendor)); fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx); fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index 2dfcd39c8..c9349a6b4 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp @@ -3,8 +3,6 @@ // Refer to the license.txt file included. #include <cstring> -#include <string> -#include <thread> #include "common/common_types.h" #include "common/x64/cpu_detect.h" @@ -51,8 +49,6 @@ namespace Common { static CPUCaps Detect() { CPUCaps caps = {}; - caps.num_cores = std::thread::hardware_concurrency(); - // Assumes the CPU supports the CPUID instruction. Those that don't would likely not support // yuzu at all anyway @@ -70,12 +66,6 @@ static CPUCaps Detect() { __cpuid(cpu_id, 0x80000000); u32 max_ex_fn = cpu_id[0]; - if (!strcmp(caps.brand_string, "GenuineIntel")) - caps.vendor = CPUVendor::INTEL; - else if (!strcmp(caps.brand_string, "AuthenticAMD")) - caps.vendor = CPUVendor::AMD; - else - caps.vendor = CPUVendor::OTHER; // Set reasonable default brand string even if brand string not available strcpy(caps.cpu_string, caps.brand_string); @@ -96,15 +86,9 @@ static CPUCaps Detect() { caps.sse4_1 = true; if ((cpu_id[2] >> 20) & 1) caps.sse4_2 = true; - if ((cpu_id[2] >> 22) & 1) - caps.movbe = true; if ((cpu_id[2] >> 25) & 1) caps.aes = true; - if ((cpu_id[3] >> 24) & 1) { - caps.fxsave_fxrstor = true; - } - // AVX support requires 3 separate checks: // - Is the AVX bit set in CPUID? // - Is the XSAVE bit set in CPUID? @@ -129,8 +113,6 @@ static CPUCaps Detect() { } } - caps.flush_to_zero = caps.sse; - if (max_ex_fn >= 0x80000004) { // Extract CPU model string __cpuid(cpu_id, 0x80000002); @@ -144,14 +126,8 @@ static CPUCaps Detect() { if (max_ex_fn >= 0x80000001) { // Check for more features __cpuid(cpu_id, 0x80000001); - if (cpu_id[2] & 1) - caps.lahf_sahf_64 = true; - if ((cpu_id[2] >> 5) & 1) - caps.lzcnt = true; if ((cpu_id[2] >> 16) & 1) caps.fma4 = true; - if ((cpu_id[3] >> 29) & 1) - caps.long_mode = true; } return caps; @@ -162,48 +138,4 @@ const CPUCaps& GetCPUCaps() { return caps; } -std::string GetCPUCapsString() { - auto caps = GetCPUCaps(); - - std::string sum(caps.cpu_string); - sum += " ("; - sum += caps.brand_string; - sum += ")"; - - if (caps.sse) - sum += ", SSE"; - if (caps.sse2) { - sum += ", SSE2"; - if (!caps.flush_to_zero) - sum += " (without DAZ)"; - } - - if (caps.sse3) - sum += ", SSE3"; - if (caps.ssse3) - sum += ", SSSE3"; - if (caps.sse4_1) - sum += ", SSE4.1"; - if (caps.sse4_2) - sum += ", SSE4.2"; - if (caps.avx) - sum += ", AVX"; - if (caps.avx2) - sum += ", AVX2"; - if (caps.bmi1) - sum += ", BMI1"; - if (caps.bmi2) - sum += ", BMI2"; - if (caps.fma) - sum += ", FMA"; - if (caps.aes) - sum += ", AES"; - if (caps.movbe) - sum += ", MOVBE"; - if (caps.long_mode) - sum += ", 64-bit support"; - - return sum; -} - } // namespace Common diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index 0af3a8adb..20f2ba234 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h @@ -4,23 +4,12 @@ #pragma once -#include <string> - namespace Common { -/// x86/x64 CPU vendors that may be detected by this module -enum class CPUVendor { - INTEL, - AMD, - OTHER, -}; - /// x86/x64 CPU capabilities that may be detected by this module struct CPUCaps { - CPUVendor vendor; char cpu_string[0x21]; char brand_string[0x41]; - int num_cores; bool sse; bool sse2; bool sse3; @@ -35,20 +24,6 @@ struct CPUCaps { bool fma; bool fma4; bool aes; - - // Support for the FXSAVE and FXRSTOR instructions - bool fxsave_fxrstor; - - bool movbe; - - // This flag indicates that the hardware supports some mode in which denormal inputs and outputs - // are automatically set to (signed) zero. - bool flush_to_zero; - - // Support for LAHF and SAHF instructions in 64-bit mode - bool lahf_sahf_64; - - bool long_mode; }; /** @@ -57,10 +32,4 @@ struct CPUCaps { */ const CPUCaps& GetCPUCaps(); -/** - * Gets a string summary of the name and supported capabilities of the host CPU - * @return String summary - */ -std::string GetCPUCapsString(); - } // namespace Common diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 142852082..729ee4a01 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -190,8 +190,11 @@ if (ENABLE_VULKAN) renderer_vulkan/vk_stream_buffer.h renderer_vulkan/vk_swapchain.cpp renderer_vulkan/vk_swapchain.h + renderer_vulkan/vk_texture_cache.cpp + renderer_vulkan/vk_texture_cache.h renderer_vulkan/vk_update_descriptor.cpp - renderer_vulkan/vk_update_descriptor.h) + renderer_vulkan/vk_update_descriptor.h + ) target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) target_compile_definitions(video_core PRIVATE HAS_VULKAN) diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 2e6c7da19..ee79260fc 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -1278,8 +1278,6 @@ public: } dirty{}; - std::array<u8, Regs::NUM_REGS> dirty_pointers{}; - /// Reads a register value located at the input method address u32 GetRegisterValue(u32 method) const; @@ -1374,6 +1372,8 @@ private: bool execute_on{true}; + std::array<u8, Regs::NUM_REGS> dirty_pointers{}; + /// Retrieves information about a specific TIC entry from the TIC buffer. Texture::TICEntry GetTICEntry(u32 tic_index) const; diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 57b57c647..6f98bd827 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -215,6 +215,18 @@ enum class F2fRoundingOp : u64 { Trunc = 11, }; +enum class AtomicOp : u64 { + Add = 0, + Min = 1, + Max = 2, + Inc = 3, + Dec = 4, + And = 5, + Or = 6, + Xor = 7, + Exch = 8, +}; + enum class UniformType : u64 { UnsignedByte = 0, SignedByte = 1, @@ -236,6 +248,13 @@ enum class StoreType : u64 { Bits128 = 6, }; +enum class AtomicType : u64 { + U32 = 0, + S32 = 1, + U64 = 2, + S64 = 3, +}; + enum class IMinMaxExchange : u64 { None = 0, XLo = 1, @@ -939,6 +958,16 @@ union Instruction { } stg; union { + BitField<52, 4, AtomicOp> operation; + BitField<28, 2, AtomicType> type; + BitField<30, 22, s64> offset; + + s32 GetImmediateOffset() const { + return static_cast<s32>(offset << 2); + } + } atoms; + + union { BitField<32, 1, PhysicalAttributeDirection> direction; BitField<47, 3, AttributeSize> size; BitField<20, 11, u64> address; @@ -1659,9 +1688,10 @@ public: ST_A, ST_L, ST_S, - ST, // Store in generic memory - STG, // Store in global memory - AL2P, // Transforms attribute memory into physical memory + ST, // Store in generic memory + STG, // Store in global memory + ATOMS, // Atomic operation on shared memory + AL2P, // Transforms attribute memory into physical memory TEX, TEX_B, // Texture Load Bindless TXQ, // Texture Query @@ -1964,6 +1994,7 @@ private: INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"), INST("101-------------", Id::ST, Type::Memory, "ST"), INST("1110111011011---", Id::STG, Type::Memory, "STG"), + INST("11101100--------", Id::ATOMS, Type::Memory, "ATOMS"), INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"), INST("110000----111---", Id::TEX, Type::Texture, "TEX"), INST("1101111010111---", Id::TEX_B, Type::Texture, "TEX_B"), diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index f9f7a97b5..19751939a 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -1856,6 +1856,16 @@ private: Type::Uint}; } + template <const std::string_view& opname, Type type> + Expression Atomic(Operation operation) { + ASSERT(stage == ShaderType::Compute); + auto& smem = std::get<SmemNode>(*operation[0]); + + return {fmt::format("atomic{}(smem[{} >> 2], {})", opname, Visit(smem.GetAddress()).AsInt(), + Visit(operation[1]).As(type)), + type}; + } + Expression Branch(Operation operation) { const auto target = std::get_if<ImmediateNode>(&*operation[0]); UNIMPLEMENTED_IF(!target); @@ -2194,6 +2204,8 @@ private: &GLSLDecompiler::AtomicImage<Func::Xor>, &GLSLDecompiler::AtomicImage<Func::Exchange>, + &GLSLDecompiler::Atomic<Func::Add, Type::Uint>, + &GLSLDecompiler::Branch, &GLSLDecompiler::BranchIndirect, &GLSLDecompiler::PushFlowStack, diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index b790b0ef4..e95eb069e 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -44,7 +44,7 @@ struct FormatTuple { constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format_tuples = {{ {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false}, // ABGR8U - {GL_RGBA8, GL_RGBA, GL_BYTE, false}, // ABGR8S + {GL_RGBA8_SNORM, GL_RGBA, GL_BYTE, false}, // ABGR8S {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, false}, // ABGR8UI {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, false}, // B5G6R5U {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, false}, // A2B10G10R10U @@ -83,9 +83,9 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format {GL_RGB32F, GL_RGB, GL_FLOAT, false}, // RGB32F {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false}, // RGBA8_SRGB {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, false}, // RG8U - {GL_RG8, GL_RG, GL_BYTE, false}, // RG8S + {GL_RG8_SNORM, GL_RG, GL_BYTE, false}, // RG8S {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, false}, // RG32UI - {GL_RGB16F, GL_RGBA16, GL_HALF_FLOAT, false}, // RGBX16F + {GL_RGB16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBX16F {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, false}, // R32UI {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X8 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X5 @@ -253,14 +253,12 @@ void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) { glPixelStorei(GL_PACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level))); glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level))); const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level); + u8* const mip_data = staging_buffer.data() + mip_offset; + const GLsizei size = static_cast<GLsizei>(params.GetHostMipmapSize(level)); if (is_compressed) { - glGetCompressedTextureImage(texture.handle, level, - static_cast<GLsizei>(params.GetHostMipmapSize(level)), - staging_buffer.data() + mip_offset); + glGetCompressedTextureImage(texture.handle, level, size, mip_data); } else { - glGetTextureImage(texture.handle, level, format, type, - static_cast<GLsizei>(params.GetHostMipmapSize(level)), - staging_buffer.data() + mip_offset); + glGetTextureImage(texture.handle, level, format, type, size, mip_data); } } } diff --git a/src/video_core/renderer_opengl/utils.cpp b/src/video_core/renderer_opengl/utils.cpp index 9770dda1c..ac99e6385 100644 --- a/src/video_core/renderer_opengl/utils.cpp +++ b/src/video_core/renderer_opengl/utils.cpp @@ -6,16 +6,20 @@ #include <vector> #include <fmt/format.h> - #include <glad/glad.h> -#include "common/assert.h" #include "common/common_types.h" -#include "common/scope_exit.h" #include "video_core/renderer_opengl/utils.h" namespace OpenGL { +struct VertexArrayPushBuffer::Entry { + GLuint binding_index{}; + const GLuint* buffer{}; + GLintptr offset{}; + GLsizei stride{}; +}; + VertexArrayPushBuffer::VertexArrayPushBuffer() = default; VertexArrayPushBuffer::~VertexArrayPushBuffer() = default; @@ -47,6 +51,13 @@ void VertexArrayPushBuffer::Bind() { } } +struct BindBuffersRangePushBuffer::Entry { + GLuint binding; + const GLuint* buffer; + GLintptr offset; + GLsizeiptr size; +}; + BindBuffersRangePushBuffer::BindBuffersRangePushBuffer(GLenum target) : target{target} {} BindBuffersRangePushBuffer::~BindBuffersRangePushBuffer() = default; diff --git a/src/video_core/renderer_opengl/utils.h b/src/video_core/renderer_opengl/utils.h index d56153fe7..3ad7c02d4 100644 --- a/src/video_core/renderer_opengl/utils.h +++ b/src/video_core/renderer_opengl/utils.h @@ -26,12 +26,7 @@ public: void Bind(); private: - struct Entry { - GLuint binding_index{}; - const GLuint* buffer{}; - GLintptr offset{}; - GLsizei stride{}; - }; + struct Entry; GLuint vao{}; const GLuint* index_buffer{}; @@ -50,12 +45,7 @@ public: void Bind(); private: - struct Entry { - GLuint binding; - const GLuint* buffer; - GLintptr offset; - GLsizeiptr size; - }; + struct Entry; GLenum target; std::vector<Entry> entries; diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 8fe852ce8..0cf97cafa 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -1796,6 +1796,11 @@ private: return {}; } + Expression UAtomicAdd(Operation) { + UNIMPLEMENTED(); + return {}; + } + Expression Branch(Operation operation) { const auto& target = std::get<ImmediateNode>(*operation[0]); OpStore(jmp_to, Constant(t_uint, target.GetValue())); @@ -2373,6 +2378,8 @@ private: &SPIRVDecompiler::AtomicImageXor, &SPIRVDecompiler::AtomicImageExchange, + &SPIRVDecompiler::UAtomicAdd, + &SPIRVDecompiler::Branch, &SPIRVDecompiler::BranchIndirect, &SPIRVDecompiler::PushFlowStack, diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h index 02310375f..4d9488f49 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h @@ -13,6 +13,7 @@ #include "video_core/renderer_vulkan/declarations.h" #include "video_core/renderer_vulkan/vk_memory_manager.h" +#include "video_core/renderer_vulkan/vk_resource_manager.h" namespace Vulkan { diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp new file mode 100644 index 000000000..51b0d38a6 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -0,0 +1,475 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <array> +#include <cstddef> +#include <cstring> +#include <memory> +#include <variant> +#include <vector> + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/common_types.h" +#include "core/core.h" +#include "core/memory.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/morton.h" +#include "video_core/renderer_vulkan/declarations.h" +#include "video_core/renderer_vulkan/maxwell_to_vk.h" +#include "video_core/renderer_vulkan/vk_device.h" +#include "video_core/renderer_vulkan/vk_memory_manager.h" +#include "video_core/renderer_vulkan/vk_rasterizer.h" +#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" +#include "video_core/renderer_vulkan/vk_texture_cache.h" +#include "video_core/surface.h" +#include "video_core/textures/convert.h" + +namespace Vulkan { + +using VideoCore::MortonSwizzle; +using VideoCore::MortonSwizzleMode; + +using Tegra::Texture::SwizzleSource; +using VideoCore::Surface::PixelFormat; +using VideoCore::Surface::SurfaceCompression; +using VideoCore::Surface::SurfaceTarget; + +namespace { + +vk::ImageType SurfaceTargetToImage(SurfaceTarget target) { + switch (target) { + case SurfaceTarget::Texture1D: + case SurfaceTarget::Texture1DArray: + return vk::ImageType::e1D; + case SurfaceTarget::Texture2D: + case SurfaceTarget::Texture2DArray: + case SurfaceTarget::TextureCubemap: + case SurfaceTarget::TextureCubeArray: + return vk::ImageType::e2D; + case SurfaceTarget::Texture3D: + return vk::ImageType::e3D; + } + UNREACHABLE_MSG("Unknown texture target={}", static_cast<u32>(target)); + return {}; +} + +vk::ImageAspectFlags PixelFormatToImageAspect(PixelFormat pixel_format) { + if (pixel_format < PixelFormat::MaxColorFormat) { + return vk::ImageAspectFlagBits::eColor; + } else if (pixel_format < PixelFormat::MaxDepthFormat) { + return vk::ImageAspectFlagBits::eDepth; + } else if (pixel_format < PixelFormat::MaxDepthStencilFormat) { + return vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil; + } else { + UNREACHABLE_MSG("Invalid pixel format={}", static_cast<u32>(pixel_format)); + return vk::ImageAspectFlagBits::eColor; + } +} + +vk::ImageViewType GetImageViewType(SurfaceTarget target) { + switch (target) { + case SurfaceTarget::Texture1D: + return vk::ImageViewType::e1D; + case SurfaceTarget::Texture2D: + return vk::ImageViewType::e2D; + case SurfaceTarget::Texture3D: + return vk::ImageViewType::e3D; + case SurfaceTarget::Texture1DArray: + return vk::ImageViewType::e1DArray; + case SurfaceTarget::Texture2DArray: + return vk::ImageViewType::e2DArray; + case SurfaceTarget::TextureCubemap: + return vk::ImageViewType::eCube; + case SurfaceTarget::TextureCubeArray: + return vk::ImageViewType::eCubeArray; + case SurfaceTarget::TextureBuffer: + break; + } + UNREACHABLE(); + return {}; +} + +UniqueBuffer CreateBuffer(const VKDevice& device, const SurfaceParams& params) { + // TODO(Rodrigo): Move texture buffer creation to the buffer cache + const vk::BufferCreateInfo buffer_ci({}, params.GetHostSizeInBytes(), + vk::BufferUsageFlagBits::eUniformTexelBuffer | + vk::BufferUsageFlagBits::eTransferSrc | + vk::BufferUsageFlagBits::eTransferDst, + vk::SharingMode::eExclusive, 0, nullptr); + const auto dev = device.GetLogical(); + const auto& dld = device.GetDispatchLoader(); + return dev.createBufferUnique(buffer_ci, nullptr, dld); +} + +vk::BufferViewCreateInfo GenerateBufferViewCreateInfo(const VKDevice& device, + const SurfaceParams& params, + vk::Buffer buffer) { + ASSERT(params.IsBuffer()); + + const auto format = + MaxwellToVK::SurfaceFormat(device, FormatType::Buffer, params.pixel_format).format; + return vk::BufferViewCreateInfo({}, buffer, format, 0, params.GetHostSizeInBytes()); +} + +vk::ImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceParams& params) { + constexpr auto sample_count = vk::SampleCountFlagBits::e1; + constexpr auto tiling = vk::ImageTiling::eOptimal; + + ASSERT(!params.IsBuffer()); + + const auto [format, attachable, storage] = + MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, params.pixel_format); + + auto image_usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst | + vk::ImageUsageFlagBits::eTransferSrc; + if (attachable) { + image_usage |= params.IsPixelFormatZeta() ? vk::ImageUsageFlagBits::eDepthStencilAttachment + : vk::ImageUsageFlagBits::eColorAttachment; + } + if (storage) { + image_usage |= vk::ImageUsageFlagBits::eStorage; + } + + vk::ImageCreateFlags flags; + vk::Extent3D extent; + switch (params.target) { + case SurfaceTarget::TextureCubemap: + case SurfaceTarget::TextureCubeArray: + flags |= vk::ImageCreateFlagBits::eCubeCompatible; + [[fallthrough]]; + case SurfaceTarget::Texture1D: + case SurfaceTarget::Texture1DArray: + case SurfaceTarget::Texture2D: + case SurfaceTarget::Texture2DArray: + extent = vk::Extent3D(params.width, params.height, 1); + break; + case SurfaceTarget::Texture3D: + extent = vk::Extent3D(params.width, params.height, params.depth); + break; + case SurfaceTarget::TextureBuffer: + UNREACHABLE(); + } + + return vk::ImageCreateInfo(flags, SurfaceTargetToImage(params.target), format, extent, + params.num_levels, static_cast<u32>(params.GetNumLayers()), + sample_count, tiling, image_usage, vk::SharingMode::eExclusive, 0, + nullptr, vk::ImageLayout::eUndefined); +} + +} // Anonymous namespace + +CachedSurface::CachedSurface(Core::System& system, const VKDevice& device, + VKResourceManager& resource_manager, VKMemoryManager& memory_manager, + VKScheduler& scheduler, VKStagingBufferPool& staging_pool, + GPUVAddr gpu_addr, const SurfaceParams& params) + : SurfaceBase<View>{gpu_addr, params}, system{system}, device{device}, + resource_manager{resource_manager}, memory_manager{memory_manager}, scheduler{scheduler}, + staging_pool{staging_pool} { + if (params.IsBuffer()) { + buffer = CreateBuffer(device, params); + commit = memory_manager.Commit(*buffer, false); + + const auto buffer_view_ci = GenerateBufferViewCreateInfo(device, params, *buffer); + format = buffer_view_ci.format; + + const auto dev = device.GetLogical(); + const auto& dld = device.GetDispatchLoader(); + buffer_view = dev.createBufferViewUnique(buffer_view_ci, nullptr, dld); + } else { + const auto image_ci = GenerateImageCreateInfo(device, params); + format = image_ci.format; + + image.emplace(device, scheduler, image_ci, PixelFormatToImageAspect(params.pixel_format)); + commit = memory_manager.Commit(image->GetHandle(), false); + } + + // TODO(Rodrigo): Move this to a virtual function. + main_view = CreateViewInner( + ViewParams(params.target, 0, static_cast<u32>(params.GetNumLayers()), 0, params.num_levels), + true); +} + +CachedSurface::~CachedSurface() = default; + +void CachedSurface::UploadTexture(const std::vector<u8>& staging_buffer) { + // To upload data we have to be outside of a renderpass + scheduler.RequestOutsideRenderPassOperationContext(); + + if (params.IsBuffer()) { + UploadBuffer(staging_buffer); + } else { + UploadImage(staging_buffer); + } +} + +void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) { + UNIMPLEMENTED_IF(params.IsBuffer()); + + if (params.pixel_format == VideoCore::Surface::PixelFormat::A1B5G5R5U) { + LOG_WARNING(Render_Vulkan, "A1B5G5R5 flushing is stubbed"); + } + + // We can't copy images to buffers inside a renderpass + scheduler.RequestOutsideRenderPassOperationContext(); + + FullTransition(vk::PipelineStageFlagBits::eTransfer, vk::AccessFlagBits::eTransferRead, + vk::ImageLayout::eTransferSrcOptimal); + + const auto& buffer = staging_pool.GetUnusedBuffer(host_memory_size, true); + // TODO(Rodrigo): Do this in a single copy + for (u32 level = 0; level < params.num_levels; ++level) { + scheduler.Record([image = image->GetHandle(), buffer = *buffer.handle, + copy = GetBufferImageCopy(level)](auto cmdbuf, auto& dld) { + cmdbuf.copyImageToBuffer(image, vk::ImageLayout::eTransferSrcOptimal, buffer, {copy}, + dld); + }); + } + scheduler.Finish(); + + // TODO(Rodrigo): Use an intern buffer for staging buffers and avoid this unnecessary memcpy. + std::memcpy(staging_buffer.data(), buffer.commit->Map(host_memory_size), host_memory_size); +} + +void CachedSurface::DecorateSurfaceName() { + // TODO(Rodrigo): Add name decorations +} + +View CachedSurface::CreateView(const ViewParams& params) { + return CreateViewInner(params, false); +} + +View CachedSurface::CreateViewInner(const ViewParams& params, bool is_proxy) { + // TODO(Rodrigo): Add name decorations + return views[params] = std::make_shared<CachedSurfaceView>(device, *this, params, is_proxy); +} + +void CachedSurface::UploadBuffer(const std::vector<u8>& staging_buffer) { + const auto& src_buffer = staging_pool.GetUnusedBuffer(host_memory_size, true); + std::memcpy(src_buffer.commit->Map(host_memory_size), staging_buffer.data(), host_memory_size); + + scheduler.Record([src_buffer = *src_buffer.handle, dst_buffer = *buffer, + size = params.GetHostSizeInBytes()](auto cmdbuf, auto& dld) { + const vk::BufferCopy copy(0, 0, size); + cmdbuf.copyBuffer(src_buffer, dst_buffer, {copy}, dld); + + cmdbuf.pipelineBarrier( + vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eVertexShader, {}, {}, + {vk::BufferMemoryBarrier(vk::AccessFlagBits::eTransferWrite, + vk::AccessFlagBits::eShaderRead, 0, 0, dst_buffer, 0, size)}, + {}, dld); + }); +} + +void CachedSurface::UploadImage(const std::vector<u8>& staging_buffer) { + const auto& src_buffer = staging_pool.GetUnusedBuffer(host_memory_size, true); + std::memcpy(src_buffer.commit->Map(host_memory_size), staging_buffer.data(), host_memory_size); + + FullTransition(vk::PipelineStageFlagBits::eTransfer, vk::AccessFlagBits::eTransferWrite, + vk::ImageLayout::eTransferDstOptimal); + + for (u32 level = 0; level < params.num_levels; ++level) { + vk::BufferImageCopy copy = GetBufferImageCopy(level); + const auto& dld = device.GetDispatchLoader(); + if (image->GetAspectMask() == + (vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil)) { + vk::BufferImageCopy depth = copy; + vk::BufferImageCopy stencil = copy; + depth.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth; + stencil.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eStencil; + scheduler.Record([buffer = *src_buffer.handle, image = image->GetHandle(), depth, + stencil](auto cmdbuf, auto& dld) { + cmdbuf.copyBufferToImage(buffer, image, vk::ImageLayout::eTransferDstOptimal, + {depth, stencil}, dld); + }); + } else { + scheduler.Record([buffer = *src_buffer.handle, image = image->GetHandle(), + copy](auto cmdbuf, auto& dld) { + cmdbuf.copyBufferToImage(buffer, image, vk::ImageLayout::eTransferDstOptimal, + {copy}, dld); + }); + } + } +} + +vk::BufferImageCopy CachedSurface::GetBufferImageCopy(u32 level) const { + const u32 vk_depth = params.target == SurfaceTarget::Texture3D ? params.GetMipDepth(level) : 1; + const auto compression_type = params.GetCompressionType(); + const std::size_t mip_offset = compression_type == SurfaceCompression::Converted + ? params.GetConvertedMipmapOffset(level) + : params.GetHostMipmapLevelOffset(level); + + return vk::BufferImageCopy( + mip_offset, 0, 0, + {image->GetAspectMask(), level, 0, static_cast<u32>(params.GetNumLayers())}, {0, 0, 0}, + {params.GetMipWidth(level), params.GetMipHeight(level), vk_depth}); +} + +vk::ImageSubresourceRange CachedSurface::GetImageSubresourceRange() const { + return {image->GetAspectMask(), 0, params.num_levels, 0, + static_cast<u32>(params.GetNumLayers())}; +} + +CachedSurfaceView::CachedSurfaceView(const VKDevice& device, CachedSurface& surface, + const ViewParams& params, bool is_proxy) + : VideoCommon::ViewBase{params}, params{surface.GetSurfaceParams()}, + image{surface.GetImageHandle()}, buffer_view{surface.GetBufferViewHandle()}, + aspect_mask{surface.GetAspectMask()}, device{device}, surface{surface}, + base_layer{params.base_layer}, num_layers{params.num_layers}, base_level{params.base_level}, + num_levels{params.num_levels}, image_view_type{image ? GetImageViewType(params.target) + : vk::ImageViewType{}} {} + +CachedSurfaceView::~CachedSurfaceView() = default; + +vk::ImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y_source, + SwizzleSource z_source, SwizzleSource w_source) { + const u32 swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source); + if (last_image_view && last_swizzle == swizzle) { + return last_image_view; + } + last_swizzle = swizzle; + + const auto [entry, is_cache_miss] = view_cache.try_emplace(swizzle); + auto& image_view = entry->second; + if (!is_cache_miss) { + return last_image_view = *image_view; + } + + auto swizzle_x = MaxwellToVK::SwizzleSource(x_source); + auto swizzle_y = MaxwellToVK::SwizzleSource(y_source); + auto swizzle_z = MaxwellToVK::SwizzleSource(z_source); + auto swizzle_w = MaxwellToVK::SwizzleSource(w_source); + + if (params.pixel_format == VideoCore::Surface::PixelFormat::A1B5G5R5U) { + // A1B5G5R5 is implemented as A1R5G5B5, we have to change the swizzle here. + std::swap(swizzle_x, swizzle_z); + } + + // Games can sample depth or stencil values on textures. This is decided by the swizzle value on + // hardware. To emulate this on Vulkan we specify it in the aspect. + vk::ImageAspectFlags aspect = aspect_mask; + if (aspect == (vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil)) { + UNIMPLEMENTED_IF(x_source != SwizzleSource::R && x_source != SwizzleSource::G); + const bool is_first = x_source == SwizzleSource::R; + switch (params.pixel_format) { + case VideoCore::Surface::PixelFormat::Z24S8: + case VideoCore::Surface::PixelFormat::Z32FS8: + aspect = is_first ? vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eStencil; + break; + case VideoCore::Surface::PixelFormat::S8Z24: + aspect = is_first ? vk::ImageAspectFlagBits::eStencil : vk::ImageAspectFlagBits::eDepth; + break; + default: + aspect = vk::ImageAspectFlagBits::eDepth; + UNIMPLEMENTED(); + } + + // Vulkan doesn't seem to understand swizzling of a depth stencil image, use identity + swizzle_x = vk::ComponentSwizzle::eR; + swizzle_y = vk::ComponentSwizzle::eG; + swizzle_z = vk::ComponentSwizzle::eB; + swizzle_w = vk::ComponentSwizzle::eA; + } + + const vk::ImageViewCreateInfo image_view_ci( + {}, surface.GetImageHandle(), image_view_type, surface.GetImage().GetFormat(), + {swizzle_x, swizzle_y, swizzle_z, swizzle_w}, + {aspect, base_level, num_levels, base_layer, num_layers}); + + const auto dev = device.GetLogical(); + image_view = dev.createImageViewUnique(image_view_ci, nullptr, device.GetDispatchLoader()); + return last_image_view = *image_view; +} + +VKTextureCache::VKTextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, + const VKDevice& device, VKResourceManager& resource_manager, + VKMemoryManager& memory_manager, VKScheduler& scheduler, + VKStagingBufferPool& staging_pool) + : TextureCache(system, rasterizer), device{device}, resource_manager{resource_manager}, + memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{staging_pool} {} + +VKTextureCache::~VKTextureCache() = default; + +Surface VKTextureCache::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) { + return std::make_shared<CachedSurface>(system, device, resource_manager, memory_manager, + scheduler, staging_pool, gpu_addr, params); +} + +void VKTextureCache::ImageCopy(Surface& src_surface, Surface& dst_surface, + const VideoCommon::CopyParams& copy_params) { + const bool src_3d = src_surface->GetSurfaceParams().target == SurfaceTarget::Texture3D; + const bool dst_3d = dst_surface->GetSurfaceParams().target == SurfaceTarget::Texture3D; + UNIMPLEMENTED_IF(src_3d); + + // The texture cache handles depth in OpenGL terms, we have to handle it as subresource and + // dimension respectively. + const u32 dst_base_layer = dst_3d ? 0 : copy_params.dest_z; + const u32 dst_offset_z = dst_3d ? copy_params.dest_z : 0; + + const u32 extent_z = dst_3d ? copy_params.depth : 1; + const u32 num_layers = dst_3d ? 1 : copy_params.depth; + + // We can't copy inside a renderpass + scheduler.RequestOutsideRenderPassOperationContext(); + + src_surface->Transition(copy_params.source_z, copy_params.depth, copy_params.source_level, 1, + vk::PipelineStageFlagBits::eTransfer, vk::AccessFlagBits::eTransferRead, + vk::ImageLayout::eTransferSrcOptimal); + dst_surface->Transition( + dst_base_layer, num_layers, copy_params.dest_level, 1, vk::PipelineStageFlagBits::eTransfer, + vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eTransferDstOptimal); + + const auto& dld{device.GetDispatchLoader()}; + const vk::ImageSubresourceLayers src_subresource( + src_surface->GetAspectMask(), copy_params.source_level, copy_params.source_z, num_layers); + const vk::ImageSubresourceLayers dst_subresource( + dst_surface->GetAspectMask(), copy_params.dest_level, dst_base_layer, num_layers); + const vk::Offset3D src_offset(copy_params.source_x, copy_params.source_y, 0); + const vk::Offset3D dst_offset(copy_params.dest_x, copy_params.dest_y, dst_offset_z); + const vk::Extent3D extent(copy_params.width, copy_params.height, extent_z); + const vk::ImageCopy copy(src_subresource, src_offset, dst_subresource, dst_offset, extent); + const vk::Image src_image = src_surface->GetImageHandle(); + const vk::Image dst_image = dst_surface->GetImageHandle(); + scheduler.Record([src_image, dst_image, copy](auto cmdbuf, auto& dld) { + cmdbuf.copyImage(src_image, vk::ImageLayout::eTransferSrcOptimal, dst_image, + vk::ImageLayout::eTransferDstOptimal, {copy}, dld); + }); +} + +void VKTextureCache::ImageBlit(View& src_view, View& dst_view, + const Tegra::Engines::Fermi2D::Config& copy_config) { + // We can't blit inside a renderpass + scheduler.RequestOutsideRenderPassOperationContext(); + + src_view->Transition(vk::ImageLayout::eTransferSrcOptimal, vk::PipelineStageFlagBits::eTransfer, + vk::AccessFlagBits::eTransferRead); + dst_view->Transition(vk::ImageLayout::eTransferDstOptimal, vk::PipelineStageFlagBits::eTransfer, + vk::AccessFlagBits::eTransferWrite); + + const auto& cfg = copy_config; + const auto src_top_left = vk::Offset3D(cfg.src_rect.left, cfg.src_rect.top, 0); + const auto src_bot_right = vk::Offset3D(cfg.src_rect.right, cfg.src_rect.bottom, 1); + const auto dst_top_left = vk::Offset3D(cfg.dst_rect.left, cfg.dst_rect.top, 0); + const auto dst_bot_right = vk::Offset3D(cfg.dst_rect.right, cfg.dst_rect.bottom, 1); + const vk::ImageBlit blit(src_view->GetImageSubresourceLayers(), {src_top_left, src_bot_right}, + dst_view->GetImageSubresourceLayers(), {dst_top_left, dst_bot_right}); + const bool is_linear = copy_config.filter == Tegra::Engines::Fermi2D::Filter::Linear; + + const auto& dld{device.GetDispatchLoader()}; + scheduler.Record([src_image = src_view->GetImage(), dst_image = dst_view->GetImage(), blit, + is_linear](auto cmdbuf, auto& dld) { + cmdbuf.blitImage(src_image, vk::ImageLayout::eTransferSrcOptimal, dst_image, + vk::ImageLayout::eTransferDstOptimal, {blit}, + is_linear ? vk::Filter::eLinear : vk::Filter::eNearest, dld); + }); +} + +void VKTextureCache::BufferCopy(Surface& src_surface, Surface& dst_surface) { + // Currently unimplemented. PBO copies should be dropped and we should use a render pass to + // convert from color to depth and viceversa. + LOG_WARNING(Render_Vulkan, "Unimplemented"); +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h new file mode 100644 index 000000000..d3edbe80c --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -0,0 +1,239 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <unordered_map> + +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/math_util.h" +#include "video_core/gpu.h" +#include "video_core/rasterizer_cache.h" +#include "video_core/renderer_vulkan/declarations.h" +#include "video_core/renderer_vulkan/vk_image.h" +#include "video_core/renderer_vulkan/vk_memory_manager.h" +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/texture_cache/surface_base.h" +#include "video_core/texture_cache/texture_cache.h" +#include "video_core/textures/decoders.h" + +namespace Core { +class System; +} + +namespace VideoCore { +class RasterizerInterface; +} + +namespace Vulkan { + +class RasterizerVulkan; +class VKDevice; +class VKResourceManager; +class VKScheduler; +class VKStagingBufferPool; + +class CachedSurfaceView; +class CachedSurface; + +using Surface = std::shared_ptr<CachedSurface>; +using View = std::shared_ptr<CachedSurfaceView>; +using TextureCacheBase = VideoCommon::TextureCache<Surface, View>; + +using VideoCommon::SurfaceParams; +using VideoCommon::ViewParams; + +class CachedSurface final : public VideoCommon::SurfaceBase<View> { + friend CachedSurfaceView; + +public: + explicit CachedSurface(Core::System& system, const VKDevice& device, + VKResourceManager& resource_manager, VKMemoryManager& memory_manager, + VKScheduler& scheduler, VKStagingBufferPool& staging_pool, + GPUVAddr gpu_addr, const SurfaceParams& params); + ~CachedSurface(); + + void UploadTexture(const std::vector<u8>& staging_buffer) override; + void DownloadTexture(std::vector<u8>& staging_buffer) override; + + void FullTransition(vk::PipelineStageFlags new_stage_mask, vk::AccessFlags new_access, + vk::ImageLayout new_layout) { + image->Transition(0, static_cast<u32>(params.GetNumLayers()), 0, params.num_levels, + new_stage_mask, new_access, new_layout); + } + + void Transition(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels, + vk::PipelineStageFlags new_stage_mask, vk::AccessFlags new_access, + vk::ImageLayout new_layout) { + image->Transition(base_layer, num_layers, base_level, num_levels, new_stage_mask, + new_access, new_layout); + } + + VKImage& GetImage() { + return *image; + } + + const VKImage& GetImage() const { + return *image; + } + + vk::Image GetImageHandle() const { + return image->GetHandle(); + } + + vk::ImageAspectFlags GetAspectMask() const { + return image->GetAspectMask(); + } + + vk::BufferView GetBufferViewHandle() const { + return *buffer_view; + } + +protected: + void DecorateSurfaceName(); + + View CreateView(const ViewParams& params) override; + View CreateViewInner(const ViewParams& params, bool is_proxy); + +private: + void UploadBuffer(const std::vector<u8>& staging_buffer); + + void UploadImage(const std::vector<u8>& staging_buffer); + + vk::BufferImageCopy GetBufferImageCopy(u32 level) const; + + vk::ImageSubresourceRange GetImageSubresourceRange() const; + + Core::System& system; + const VKDevice& device; + VKResourceManager& resource_manager; + VKMemoryManager& memory_manager; + VKScheduler& scheduler; + VKStagingBufferPool& staging_pool; + + std::optional<VKImage> image; + UniqueBuffer buffer; + UniqueBufferView buffer_view; + VKMemoryCommit commit; + + vk::Format format; +}; + +class CachedSurfaceView final : public VideoCommon::ViewBase { +public: + explicit CachedSurfaceView(const VKDevice& device, CachedSurface& surface, + const ViewParams& params, bool is_proxy); + ~CachedSurfaceView(); + + vk::ImageView GetHandle(Tegra::Texture::SwizzleSource x_source, + Tegra::Texture::SwizzleSource y_source, + Tegra::Texture::SwizzleSource z_source, + Tegra::Texture::SwizzleSource w_source); + + bool IsSameSurface(const CachedSurfaceView& rhs) const { + return &surface == &rhs.surface; + } + + vk::ImageView GetHandle() { + return GetHandle(Tegra::Texture::SwizzleSource::R, Tegra::Texture::SwizzleSource::G, + Tegra::Texture::SwizzleSource::B, Tegra::Texture::SwizzleSource::A); + } + + u32 GetWidth() const { + return params.GetMipWidth(base_level); + } + + u32 GetHeight() const { + return params.GetMipHeight(base_level); + } + + bool IsBufferView() const { + return buffer_view; + } + + vk::Image GetImage() const { + return image; + } + + vk::BufferView GetBufferView() const { + return buffer_view; + } + + vk::ImageSubresourceRange GetImageSubresourceRange() const { + return {aspect_mask, base_level, num_levels, base_layer, num_layers}; + } + + vk::ImageSubresourceLayers GetImageSubresourceLayers() const { + return {surface.GetAspectMask(), base_level, base_layer, num_layers}; + } + + void Transition(vk::ImageLayout new_layout, vk::PipelineStageFlags new_stage_mask, + vk::AccessFlags new_access) const { + surface.Transition(base_layer, num_layers, base_level, num_levels, new_stage_mask, + new_access, new_layout); + } + + void MarkAsModified(u64 tick) { + surface.MarkAsModified(true, tick); + } + +private: + static u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source, + Tegra::Texture::SwizzleSource y_source, + Tegra::Texture::SwizzleSource z_source, + Tegra::Texture::SwizzleSource w_source) { + return (static_cast<u32>(x_source) << 24) | (static_cast<u32>(y_source) << 16) | + (static_cast<u32>(z_source) << 8) | static_cast<u32>(w_source); + } + + // Store a copy of these values to avoid double dereference when reading them + const SurfaceParams params; + const vk::Image image; + const vk::BufferView buffer_view; + const vk::ImageAspectFlags aspect_mask; + + const VKDevice& device; + CachedSurface& surface; + const u32 base_layer; + const u32 num_layers; + const u32 base_level; + const u32 num_levels; + const vk::ImageViewType image_view_type; + + vk::ImageView last_image_view; + u32 last_swizzle{}; + + std::unordered_map<u32, UniqueImageView> view_cache; +}; + +class VKTextureCache final : public TextureCacheBase { +public: + explicit VKTextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, + const VKDevice& device, VKResourceManager& resource_manager, + VKMemoryManager& memory_manager, VKScheduler& scheduler, + VKStagingBufferPool& staging_pool); + ~VKTextureCache(); + +private: + Surface CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) override; + + void ImageCopy(Surface& src_surface, Surface& dst_surface, + const VideoCommon::CopyParams& copy_params) override; + + void ImageBlit(View& src_view, View& dst_view, + const Tegra::Engines::Fermi2D::Config& copy_config) override; + + void BufferCopy(Surface& src_surface, Surface& dst_surface) override; + + const VKDevice& device; + VKResourceManager& resource_manager; + VKMemoryManager& memory_manager; + VKScheduler& scheduler; + VKStagingBufferPool& staging_pool; +}; + +} // namespace Vulkan diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp index 8cc84e935..7591a715f 100644 --- a/src/video_core/shader/decode/memory.cpp +++ b/src/video_core/shader/decode/memory.cpp @@ -16,6 +16,8 @@ namespace VideoCommon::Shader { +using Tegra::Shader::AtomicOp; +using Tegra::Shader::AtomicType; using Tegra::Shader::Attribute; using Tegra::Shader::Instruction; using Tegra::Shader::OpCode; @@ -333,6 +335,23 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { } break; } + case OpCode::Id::ATOMS: { + UNIMPLEMENTED_IF_MSG(instr.atoms.operation != AtomicOp::Add, "operation={}", + static_cast<int>(instr.atoms.operation.Value())); + UNIMPLEMENTED_IF_MSG(instr.atoms.type != AtomicType::U32, "type={}", + static_cast<int>(instr.atoms.type.Value())); + + const s32 offset = instr.atoms.GetImmediateOffset(); + Node address = GetRegister(instr.gpr8); + address = Operation(OperationCode::IAdd, std::move(address), Immediate(offset)); + + Node memory = GetSharedMemory(std::move(address)); + Node data = GetRegister(instr.gpr20); + + Node value = Operation(OperationCode::UAtomicAdd, std::move(memory), std::move(data)); + SetRegister(bb, instr.gpr0, std::move(value)); + break; + } case OpCode::Id::AL2P: { // Ignore al2p.direction since we don't care about it. diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index 4e155542a..075c7d07c 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h @@ -162,6 +162,8 @@ enum class OperationCode { AtomicImageXor, /// (MetaImage, int[N] coords) -> void AtomicImageExchange, /// (MetaImage, int[N] coords) -> void + UAtomicAdd, /// (smem, uint) -> uint + Branch, /// (uint branch_target) -> void BranchIndirect, /// (uint branch_target) -> void PushFlowStack, /// (uint branch_target) -> void diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index 271e67533..81fb9f633 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp @@ -95,7 +95,7 @@ constexpr std::array<Table, 74> DefinitionTable = {{ {TextureFormat::ZF32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::Z32F}, {TextureFormat::Z16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::Z16}, {TextureFormat::S8Z24, C, UINT, UNORM, UNORM, UNORM, PixelFormat::S8Z24}, - {TextureFormat::ZF32_X24S8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::Z32FS8}, + {TextureFormat::ZF32_X24S8, C, FLOAT, UINT, UNORM, UNORM, PixelFormat::Z32FS8}, {TextureFormat::DXT1, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::DXT1}, {TextureFormat::DXT1, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::DXT1_SRGB}, diff --git a/src/video_core/texture_cache/surface_params.h b/src/video_core/texture_cache/surface_params.h index 992b5c022..9256fd6d9 100644 --- a/src/video_core/texture_cache/surface_params.h +++ b/src/video_core/texture_cache/surface_params.h @@ -209,6 +209,11 @@ public: return target == VideoCore::Surface::SurfaceTarget::TextureBuffer; } + /// Returns the number of layers in the surface. + std::size_t GetNumLayers() const { + return is_layered ? depth : 1; + } + /// Returns the debug name of the texture for use in graphic debuggers. std::string TargetName() const; @@ -287,10 +292,6 @@ private: /// Returns the size of a layer std::size_t GetLayerSize(bool as_host_size, bool uncompressed) const; - std::size_t GetNumLayers() const { - return is_layered ? depth : 1; - } - /// Returns true if these parameters are from a layered surface. bool IsLayered() const; }; diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 21f422500..dd5371059 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -15,7 +15,7 @@ </property> <property name="windowIcon"> <iconset> - <normaloff>src/pcafe/res/icon3_64x64.ico</normaloff>src/pcafe/res/icon3_64x64.ico</iconset> + <normaloff>../dist/yuzu.ico</normaloff>../dist/yuzu.ico</iconset> </property> <property name="tabShape"> <enum>QTabWidget::Rounded</enum> |