diff options
31 files changed, 690 insertions, 669 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp index c45fb960c..d697b80ef 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -46,7 +46,6 @@ #include "core/settings.h" #include "core/telemetry_session.h" #include "core/tools/freezer.h" -#include "video_core/debug_utils/debug_utils.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" @@ -341,7 +340,6 @@ struct System::Impl { std::unique_ptr<Loader::AppLoader> app_loader; std::unique_ptr<VideoCore::RendererBase> renderer; std::unique_ptr<Tegra::GPU> gpu_core; - std::shared_ptr<Tegra::DebugContext> debug_context; std::unique_ptr<Hardware::InterruptManager> interrupt_manager; Memory::Memory memory; CpuCoreManager cpu_core_manager; @@ -580,14 +578,6 @@ Loader::AppLoader& System::GetAppLoader() const { return *impl->app_loader; } -void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) { - impl->debug_context = std::move(context); -} - -Tegra::DebugContext* System::GetGPUDebugContext() const { - return impl->debug_context.get(); -} - void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { impl->virtual_filesystem = std::move(vfs); } diff --git a/src/core/core.h b/src/core/core.h index 91184e433..e240c5c58 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -307,10 +307,6 @@ public: Service::SM::ServiceManager& ServiceManager(); const Service::SM::ServiceManager& ServiceManager() const; - void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context); - - Tegra::DebugContext* GetGPUDebugContext() const; - void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs); std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 2e53b3221..767158444 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -9,6 +9,7 @@ #include "core/hle/kernel/writable_event.h" #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/service.h" +#include "core/settings.h" namespace Service::NIFM { @@ -86,7 +87,12 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.PushEnum(RequestState::Connected); + + if (Settings::values.bcat_backend == "none") { + rb.PushEnum(RequestState::NotSubmitted); + } else { + rb.PushEnum(RequestState::Connected); + } } void GetResult(Kernel::HLERequestContext& ctx) { @@ -194,14 +200,22 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u8>(1); + if (Settings::values.bcat_backend == "none") { + rb.Push<u8>(0); + } else { + rb.Push<u8>(1); + } } void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u8>(1); + if (Settings::values.bcat_backend == "none") { + rb.Push<u8>(0); + } else { + rb.Push<u8>(1); + } } Core::System& system; }; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 07c88465e..195421cc0 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -104,10 +104,12 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) ASSERT(object->status == nvmap::Object::Status::Allocated); - u64 size = static_cast<u64>(entry.pages) << 0x10; + const u64 size = static_cast<u64>(entry.pages) << 0x10; ASSERT(size <= object->size); + const u64 map_offset = static_cast<u64>(entry.map_offset) << 0x10; - GPUVAddr returned = gpu.MemoryManager().MapBufferEx(object->addr, offset, size); + const GPUVAddr returned = + gpu.MemoryManager().MapBufferEx(object->addr + map_offset, offset, size); ASSERT(returned == offset); } std::memcpy(output.data(), entries.data(), output.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 169fb8f0e..f79fcc065 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -62,7 +62,7 @@ private: u16_le flags; u16_le kind; u32_le nvmap_handle; - INSERT_PADDING_WORDS(1); + u32_le map_offset; u32_le offset; u32_le pages; }; diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index e615b238e..abbf218f5 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -4,8 +4,6 @@ add_library(video_core STATIC buffer_cache/map_interval.h dma_pusher.cpp dma_pusher.h - debug_utils/debug_utils.cpp - debug_utils/debug_utils.h engines/const_buffer_engine_interface.h engines/const_buffer_info.h engines/engine_upload.cpp @@ -157,8 +155,12 @@ if (ENABLE_VULKAN) renderer_vulkan/maxwell_to_vk.h renderer_vulkan/vk_buffer_cache.cpp renderer_vulkan/vk_buffer_cache.h + renderer_vulkan/vk_descriptor_pool.cpp + renderer_vulkan/vk_descriptor_pool.h renderer_vulkan/vk_device.cpp renderer_vulkan/vk_device.h + renderer_vulkan/vk_image.cpp + renderer_vulkan/vk_image.h renderer_vulkan/vk_memory_manager.cpp renderer_vulkan/vk_memory_manager.h renderer_vulkan/vk_resource_manager.cpp @@ -169,6 +171,8 @@ if (ENABLE_VULKAN) renderer_vulkan/vk_scheduler.h renderer_vulkan/vk_shader_decompiler.cpp renderer_vulkan/vk_shader_decompiler.h + renderer_vulkan/vk_staging_buffer_pool.cpp + renderer_vulkan/vk_staging_buffer_pool.h renderer_vulkan/vk_stream_buffer.cpp renderer_vulkan/vk_stream_buffer.h renderer_vulkan/vk_swapchain.cpp diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp deleted file mode 100644 index f0ef67535..000000000 --- a/src/video_core/debug_utils/debug_utils.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#include <mutex> - -#include "video_core/debug_utils/debug_utils.h" - -namespace Tegra { - -void DebugContext::DoOnEvent(Event event, void* data) { - { - std::unique_lock lock{breakpoint_mutex}; - - // TODO(Subv): Commit the rasterizer's caches so framebuffers, render targets, etc. will - // show on debug widgets - - // TODO: Should stop the CPU thread here once we multithread emulation. - - active_breakpoint = event; - at_breakpoint = true; - - // Tell all observers that we hit a breakpoint - for (auto& breakpoint_observer : breakpoint_observers) { - breakpoint_observer->OnMaxwellBreakPointHit(event, data); - } - - // Wait until another thread tells us to Resume() - resume_from_breakpoint.wait(lock, [&] { return !at_breakpoint; }); - } -} - -void DebugContext::Resume() { - { - std::lock_guard lock{breakpoint_mutex}; - - // Tell all observers that we are about to resume - for (auto& breakpoint_observer : breakpoint_observers) { - breakpoint_observer->OnMaxwellResume(); - } - - // Resume the waiting thread (i.e. OnEvent()) - at_breakpoint = false; - } - - resume_from_breakpoint.notify_one(); -} - -} // namespace Tegra diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h deleted file mode 100644 index ac3a2eb01..000000000 --- a/src/video_core/debug_utils/debug_utils.h +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#pragma once - -#include <array> -#include <condition_variable> -#include <list> -#include <memory> -#include <mutex> - -namespace Tegra { - -class DebugContext { -public: - enum class Event { - FirstEvent = 0, - - MaxwellCommandLoaded = FirstEvent, - MaxwellCommandProcessed, - IncomingPrimitiveBatch, - FinishedPrimitiveBatch, - - NumEvents - }; - - /** - * Inherit from this class to be notified of events registered to some debug context. - * Most importantly this is used for our debugger GUI. - * - * To implement event handling, override the OnMaxwellBreakPointHit and OnMaxwellResume methods. - * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state - * access - * @todo Evaluate an alternative interface, in which there is only one managing observer and - * multiple child observers running (by design) on the same thread. - */ - class BreakPointObserver { - public: - /// Constructs the object such that it observes events of the given DebugContext. - explicit BreakPointObserver(std::shared_ptr<DebugContext> debug_context) - : context_weak(debug_context) { - std::unique_lock lock{debug_context->breakpoint_mutex}; - debug_context->breakpoint_observers.push_back(this); - } - - virtual ~BreakPointObserver() { - auto context = context_weak.lock(); - if (context) { - { - std::unique_lock lock{context->breakpoint_mutex}; - context->breakpoint_observers.remove(this); - } - - // If we are the last observer to be destroyed, tell the debugger context that - // it is free to continue. In particular, this is required for a proper yuzu - // shutdown, when the emulation thread is waiting at a breakpoint. - if (context->breakpoint_observers.empty()) - context->Resume(); - } - } - - /** - * Action to perform when a breakpoint was reached. - * @param event Type of event which triggered the breakpoint - * @param data Optional data pointer (if unused, this is a nullptr) - * @note This function will perform nothing unless it is overridden in the child class. - */ - virtual void OnMaxwellBreakPointHit(Event event, void* data) {} - - /** - * Action to perform when emulation is resumed from a breakpoint. - * @note This function will perform nothing unless it is overridden in the child class. - */ - virtual void OnMaxwellResume() {} - - protected: - /** - * Weak context pointer. This need not be valid, so when requesting a shared_ptr via - * context_weak.lock(), always compare the result against nullptr. - */ - std::weak_ptr<DebugContext> context_weak; - }; - - /** - * Simple structure defining a breakpoint state - */ - struct BreakPoint { - bool enabled = false; - }; - - /** - * Static constructor used to create a shared_ptr of a DebugContext. - */ - static std::shared_ptr<DebugContext> Construct() { - return std::shared_ptr<DebugContext>(new DebugContext); - } - - /** - * Used by the emulation core when a given event has happened. If a breakpoint has been set - * for this event, OnEvent calls the event handlers of the registered breakpoint observers. - * The current thread then is halted until Resume() is called from another thread (or until - * emulation is stopped). - * @param event Event which has happened - * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until - * Resume() is called. - */ - void OnEvent(Event event, void* data) { - // This check is left in the header to allow the compiler to inline it. - if (!breakpoints[(int)event].enabled) - return; - // For the rest of event handling, call a separate function. - DoOnEvent(event, data); - } - - void DoOnEvent(Event event, void* data); - - /** - * Resume from the current breakpoint. - * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock. - * Calling from any other thread is safe. - */ - void Resume(); - - /** - * Delete all set breakpoints and resume emulation. - */ - void ClearBreakpoints() { - for (auto& bp : breakpoints) { - bp.enabled = false; - } - Resume(); - } - - // TODO: Evaluate if access to these members should be hidden behind a public interface. - std::array<BreakPoint, static_cast<int>(Event::NumEvents)> breakpoints; - Event active_breakpoint{}; - bool at_breakpoint = false; - -private: - /** - * Private default constructor to make sure people always construct this through Construct() - * instead. - */ - DebugContext() = default; - - /// Mutex protecting current breakpoint state and the observer list. - std::mutex breakpoint_mutex; - - /// Used by OnEvent to wait for resumption. - std::condition_variable resume_from_breakpoint; - - /// List of registered observers - std::list<BreakPointObserver*> breakpoint_observers; -}; - -} // namespace Tegra diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index e1cb8b0b0..1d1f780e7 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -7,7 +7,6 @@ #include "common/assert.h" #include "core/core.h" #include "core/core_timing.h" -#include "video_core/debug_utils/debug_utils.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/engines/shader_type.h" #include "video_core/memory_manager.h" @@ -273,8 +272,6 @@ void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u3 } void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { - auto debug_context = system.GetGPUDebugContext(); - const u32 method = method_call.method; if (method == cb_data_state.current) { @@ -315,10 +312,6 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Maxwell3D register, increase the size of the Regs structure"); - if (debug_context) { - debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr); - } - if (regs.reg_array[method] != method_call.argument) { regs.reg_array[method] = method_call.argument; const std::size_t dirty_reg = dirty_pointers[method]; @@ -424,10 +417,6 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { default: break; } - - if (debug_context) { - debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr); - } } void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) { @@ -485,12 +474,6 @@ void Maxwell3D::FlushMMEInlineDraw() { ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); ASSERT(mme_draw.instance_count == mme_draw.gl_end_count); - auto debug_context = system.GetGPUDebugContext(); - - if (debug_context) { - debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr); - } - // Both instance configuration registers can not be set at the same time. ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont, "Illegal combination of instancing parameters"); @@ -500,10 +483,6 @@ void Maxwell3D::FlushMMEInlineDraw() { rasterizer.DrawMultiBatch(is_indexed); } - if (debug_context) { - debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr); - } - // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if // the game is trying to draw indexed or direct mode. This needs to be verified on HW still - // it's possible that it is incorrect and that there is some other register used to specify the @@ -650,12 +629,6 @@ void Maxwell3D::DrawArrays() { regs.vertex_buffer.count); ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); - auto debug_context = system.GetGPUDebugContext(); - - if (debug_context) { - debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr); - } - // Both instance configuration registers can not be set at the same time. ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont, "Illegal combination of instancing parameters"); @@ -673,10 +646,6 @@ void Maxwell3D::DrawArrays() { rasterizer.DrawBatch(is_indexed); } - if (debug_context) { - debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr); - } - // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if // the game is trying to draw indexed or direct mode. This needs to be verified on HW still - // it's possible that it is incorrect and that there is some other register used to specify the diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 412ca5551..57b57c647 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -1051,7 +1051,7 @@ union Instruction { BitField<40, 1, R2pMode> mode; BitField<41, 2, u64> byte; BitField<20, 7, u64> immediate_mask; - } r2p; + } p2r_r2p; union { BitField<39, 3, u64> pred39; @@ -1801,6 +1801,7 @@ public: PSET, CSETP, R2P_IMM, + P2R_IMM, XMAD_IMM, XMAD_CR, XMAD_RC, @@ -2106,6 +2107,7 @@ private: INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"), INST("010100001010----", Id::CSETP, Type::PredicateSetPredicate, "CSETP"), INST("0011100-11110---", Id::R2P_IMM, Type::RegisterSetPredicate, "R2P_IMM"), + INST("0011100-11101---", Id::P2R_IMM, Type::RegisterSetPredicate, "P2R_IMM"), INST("0011011-00------", Id::XMAD_IMM, Type::Xmad, "XMAD_IMM"), INST("0100111---------", Id::XMAD_CR, Type::Xmad, "XMAD_CR"), INST("010100010-------", Id::XMAD_RC, Type::Xmad, "XMAD_RC"), diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index a311dbcfe..f9f7a97b5 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -751,6 +751,9 @@ private: Expression Visit(const Node& node) { if (const auto operation = std::get_if<OperationNode>(&*node)) { + if (const auto amend_index = operation->GetAmendIndex()) { + Visit(ir.GetAmendNode(*amend_index)).CheckVoid(); + } const auto operation_index = static_cast<std::size_t>(operation->GetCode()); if (operation_index >= operation_decompilers.size()) { UNREACHABLE_MSG("Out of bounds operation: {}", operation_index); @@ -872,6 +875,9 @@ private: } if (const auto conditional = std::get_if<ConditionalNode>(&*node)) { + if (const auto amend_index = conditional->GetAmendIndex()) { + Visit(ir.GetAmendNode(*amend_index)).CheckVoid(); + } // It's invalid to call conditional on nested nodes, use an operation instead code.AddLine("if ({}) {{", Visit(conditional->GetCondition()).AsBool()); ++code.scope; diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 9ed738171..ea4f35663 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -120,6 +120,8 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { return GL_POINTS; case Maxwell::PrimitiveTopology::Lines: return GL_LINES; + case Maxwell::PrimitiveTopology::LineLoop: + return GL_LINE_LOOP; case Maxwell::PrimitiveTopology::LineStrip: return GL_LINE_STRIP; case Maxwell::PrimitiveTopology::Triangles: @@ -130,11 +132,23 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { return GL_TRIANGLE_FAN; case Maxwell::PrimitiveTopology::Quads: return GL_QUADS; - default: - LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology)); - UNREACHABLE(); - return {}; + case Maxwell::PrimitiveTopology::QuadStrip: + return GL_QUAD_STRIP; + case Maxwell::PrimitiveTopology::Polygon: + return GL_POLYGON; + case Maxwell::PrimitiveTopology::LinesAdjacency: + return GL_LINES_ADJACENCY; + case Maxwell::PrimitiveTopology::LineStripAdjacency: + return GL_LINE_STRIP_ADJACENCY; + case Maxwell::PrimitiveTopology::TrianglesAdjacency: + return GL_TRIANGLES_ADJACENCY; + case Maxwell::PrimitiveTopology::TriangleStripAdjacency: + return GL_TRIANGLE_STRIP_ADJACENCY; + case Maxwell::PrimitiveTopology::Patches: + return GL_PATCHES; } + UNREACHABLE_MSG("Invalid topology={}", static_cast<int>(topology)); + return GL_POINTS; } inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp new file mode 100644 index 000000000..cc7c281a0 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp @@ -0,0 +1,89 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> +#include <vector> + +#include "common/common_types.h" +#include "video_core/renderer_vulkan/declarations.h" +#include "video_core/renderer_vulkan/vk_descriptor_pool.h" +#include "video_core/renderer_vulkan/vk_device.h" +#include "video_core/renderer_vulkan/vk_resource_manager.h" + +namespace Vulkan { + +// Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines. +constexpr std::size_t SETS_GROW_RATE = 0x20; + +DescriptorAllocator::DescriptorAllocator(VKDescriptorPool& descriptor_pool, + vk::DescriptorSetLayout layout) + : VKFencedPool{SETS_GROW_RATE}, descriptor_pool{descriptor_pool}, layout{layout} {} + +DescriptorAllocator::~DescriptorAllocator() = default; + +vk::DescriptorSet DescriptorAllocator::Commit(VKFence& fence) { + return *descriptors[CommitResource(fence)]; +} + +void DescriptorAllocator::Allocate(std::size_t begin, std::size_t end) { + auto new_sets = descriptor_pool.AllocateDescriptors(layout, end - begin); + descriptors.insert(descriptors.end(), std::make_move_iterator(new_sets.begin()), + std::make_move_iterator(new_sets.end())); +} + +VKDescriptorPool::VKDescriptorPool(const VKDevice& device) + : device{device}, active_pool{AllocateNewPool()} {} + +VKDescriptorPool::~VKDescriptorPool() = default; + +vk::DescriptorPool VKDescriptorPool::AllocateNewPool() { + static constexpr u32 num_sets = 0x20000; + static constexpr vk::DescriptorPoolSize pool_sizes[] = { + {vk::DescriptorType::eUniformBuffer, num_sets * 90}, + {vk::DescriptorType::eStorageBuffer, num_sets * 60}, + {vk::DescriptorType::eUniformTexelBuffer, num_sets * 64}, + {vk::DescriptorType::eCombinedImageSampler, num_sets * 64}, + {vk::DescriptorType::eStorageImage, num_sets * 40}}; + + const vk::DescriptorPoolCreateInfo create_info( + vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, num_sets, + static_cast<u32>(std::size(pool_sizes)), std::data(pool_sizes)); + const auto dev = device.GetLogical(); + return *pools.emplace_back( + dev.createDescriptorPoolUnique(create_info, nullptr, device.GetDispatchLoader())); +} + +std::vector<UniqueDescriptorSet> VKDescriptorPool::AllocateDescriptors( + vk::DescriptorSetLayout layout, std::size_t count) { + std::vector layout_copies(count, layout); + vk::DescriptorSetAllocateInfo allocate_info(active_pool, static_cast<u32>(count), + layout_copies.data()); + + std::vector<vk::DescriptorSet> sets(count); + const auto dev = device.GetLogical(); + const auto& dld = device.GetDispatchLoader(); + switch (const auto result = dev.allocateDescriptorSets(&allocate_info, sets.data(), dld)) { + case vk::Result::eSuccess: + break; + case vk::Result::eErrorOutOfPoolMemory: + active_pool = AllocateNewPool(); + allocate_info.descriptorPool = active_pool; + if (dev.allocateDescriptorSets(&allocate_info, sets.data(), dld) == vk::Result::eSuccess) { + break; + } + [[fallthrough]]; + default: + vk::throwResultException(result, "vk::Device::allocateDescriptorSetsUnique"); + } + + vk::PoolFree deleter(dev, active_pool, dld); + std::vector<UniqueDescriptorSet> unique_sets; + unique_sets.reserve(count); + for (const auto set : sets) { + unique_sets.push_back(UniqueDescriptorSet{set, deleter}); + } + return unique_sets; +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.h b/src/video_core/renderer_vulkan/vk_descriptor_pool.h new file mode 100644 index 000000000..a441dbc0f --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.h @@ -0,0 +1,56 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <vector> + +#include "common/common_types.h" +#include "video_core/renderer_vulkan/declarations.h" +#include "video_core/renderer_vulkan/vk_resource_manager.h" + +namespace Vulkan { + +class VKDescriptorPool; + +class DescriptorAllocator final : public VKFencedPool { +public: + explicit DescriptorAllocator(VKDescriptorPool& descriptor_pool, vk::DescriptorSetLayout layout); + ~DescriptorAllocator() override; + + DescriptorAllocator(const DescriptorAllocator&) = delete; + + vk::DescriptorSet Commit(VKFence& fence); + +protected: + void Allocate(std::size_t begin, std::size_t end) override; + +private: + VKDescriptorPool& descriptor_pool; + const vk::DescriptorSetLayout layout; + + std::vector<UniqueDescriptorSet> descriptors; +}; + +class VKDescriptorPool final { + friend DescriptorAllocator; + +public: + explicit VKDescriptorPool(const VKDevice& device); + ~VKDescriptorPool(); + +private: + vk::DescriptorPool AllocateNewPool(); + + std::vector<UniqueDescriptorSet> AllocateDescriptors(vk::DescriptorSetLayout layout, + std::size_t count); + + const VKDevice& device; + + std::vector<UniqueDescriptorPool> pools; + vk::DescriptorPool active_pool; +}; + +} // namespace Vulkan
\ No newline at end of file diff --git a/src/video_core/renderer_vulkan/vk_image.cpp b/src/video_core/renderer_vulkan/vk_image.cpp new file mode 100644 index 000000000..4bcbef959 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_image.cpp @@ -0,0 +1,106 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> +#include <vector> + +#include "common/assert.h" +#include "video_core/renderer_vulkan/declarations.h" +#include "video_core/renderer_vulkan/vk_device.h" +#include "video_core/renderer_vulkan/vk_image.h" +#include "video_core/renderer_vulkan/vk_scheduler.h" + +namespace Vulkan { + +VKImage::VKImage(const VKDevice& device, VKScheduler& scheduler, + const vk::ImageCreateInfo& image_ci, vk::ImageAspectFlags aspect_mask) + : device{device}, scheduler{scheduler}, format{image_ci.format}, aspect_mask{aspect_mask}, + image_num_layers{image_ci.arrayLayers}, image_num_levels{image_ci.mipLevels} { + UNIMPLEMENTED_IF_MSG(image_ci.queueFamilyIndexCount != 0, + "Queue family tracking is not implemented"); + + const auto dev = device.GetLogical(); + image = dev.createImageUnique(image_ci, nullptr, device.GetDispatchLoader()); + + const u32 num_ranges = image_num_layers * image_num_levels; + barriers.resize(num_ranges); + subrange_states.resize(num_ranges, {{}, image_ci.initialLayout}); +} + +VKImage::~VKImage() = default; + +void VKImage::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) { + if (!HasChanged(base_layer, num_layers, base_level, num_levels, new_access, new_layout)) { + return; + } + + std::size_t cursor = 0; + for (u32 layer_it = 0; layer_it < num_layers; ++layer_it) { + for (u32 level_it = 0; level_it < num_levels; ++level_it, ++cursor) { + const u32 layer = base_layer + layer_it; + const u32 level = base_level + level_it; + auto& state = GetSubrangeState(layer, level); + barriers[cursor] = vk::ImageMemoryBarrier( + state.access, new_access, state.layout, new_layout, VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, *image, {aspect_mask, level, 1, layer, 1}); + state.access = new_access; + state.layout = new_layout; + } + } + + scheduler.RequestOutsideRenderPassOperationContext(); + + scheduler.Record([barriers = barriers, cursor](auto cmdbuf, auto& dld) { + // TODO(Rodrigo): Implement a way to use the latest stage across subresources. + constexpr auto stage_stub = vk::PipelineStageFlagBits::eAllCommands; + cmdbuf.pipelineBarrier(stage_stub, stage_stub, {}, 0, nullptr, 0, nullptr, + static_cast<u32>(cursor), barriers.data(), dld); + }); +} + +bool VKImage::HasChanged(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels, + vk::AccessFlags new_access, vk::ImageLayout new_layout) noexcept { + const bool is_full_range = base_layer == 0 && num_layers == image_num_layers && + base_level == 0 && num_levels == image_num_levels; + if (!is_full_range) { + state_diverged = true; + } + + if (!state_diverged) { + auto& state = GetSubrangeState(0, 0); + if (state.access != new_access || state.layout != new_layout) { + return true; + } + } + + for (u32 layer_it = 0; layer_it < num_layers; ++layer_it) { + for (u32 level_it = 0; level_it < num_levels; ++level_it) { + const u32 layer = base_layer + layer_it; + const u32 level = base_level + level_it; + auto& state = GetSubrangeState(layer, level); + if (state.access != new_access || state.layout != new_layout) { + return true; + } + } + } + return false; +} + +void VKImage::CreatePresentView() { + // Image type has to be 2D to be presented. + const vk::ImageViewCreateInfo image_view_ci({}, *image, vk::ImageViewType::e2D, format, {}, + {aspect_mask, 0, 1, 0, 1}); + const auto dev = device.GetLogical(); + const auto& dld = device.GetDispatchLoader(); + present_view = dev.createImageViewUnique(image_view_ci, nullptr, dld); +} + +VKImage::SubrangeState& VKImage::GetSubrangeState(u32 layer, u32 level) noexcept { + return subrange_states[static_cast<std::size_t>(layer * image_num_levels) + + static_cast<std::size_t>(level)]; +} + +} // namespace Vulkan
\ No newline at end of file diff --git a/src/video_core/renderer_vulkan/vk_image.h b/src/video_core/renderer_vulkan/vk_image.h new file mode 100644 index 000000000..b78242512 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_image.h @@ -0,0 +1,84 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <vector> + +#include "common/common_types.h" +#include "video_core/renderer_vulkan/declarations.h" + +namespace Vulkan { + +class VKDevice; +class VKScheduler; + +class VKImage { +public: + explicit VKImage(const VKDevice& device, VKScheduler& scheduler, + const vk::ImageCreateInfo& image_ci, vk::ImageAspectFlags aspect_mask); + ~VKImage(); + + /// Records in the passed command buffer an image transition and updates the state of the image. + 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); + + /// Returns a view compatible with presentation, the image has to be 2D. + vk::ImageView GetPresentView() { + if (!present_view) { + CreatePresentView(); + } + return *present_view; + } + + /// Returns the Vulkan image handler. + vk::Image GetHandle() const { + return *image; + } + + /// Returns the Vulkan format for this image. + vk::Format GetFormat() const { + return format; + } + + /// Returns the Vulkan aspect mask. + vk::ImageAspectFlags GetAspectMask() const { + return aspect_mask; + } + +private: + struct SubrangeState final { + vk::AccessFlags access{}; ///< Current access bits. + vk::ImageLayout layout = vk::ImageLayout::eUndefined; ///< Current image layout. + }; + + bool HasChanged(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels, + vk::AccessFlags new_access, vk::ImageLayout new_layout) noexcept; + + /// Creates a presentation view. + void CreatePresentView(); + + /// Returns the subrange state for a layer and layer. + SubrangeState& GetSubrangeState(u32 layer, u32 level) noexcept; + + const VKDevice& device; ///< Device handler. + VKScheduler& scheduler; ///< Device scheduler. + + const vk::Format format; ///< Vulkan format. + const vk::ImageAspectFlags aspect_mask; ///< Vulkan aspect mask. + const u32 image_num_layers; ///< Number of layers. + const u32 image_num_levels; ///< Number of mipmap levels. + + UniqueImage image; ///< Image handle. + UniqueImageView present_view; ///< Image view compatible with presentation. + + std::vector<vk::ImageMemoryBarrier> barriers; ///< Pool of barriers. + std::vector<SubrangeState> subrange_states; ///< Current subrange state. + + bool state_diverged = false; ///< True when subresources mismatch in layout. +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index a8baf91de..8fe852ce8 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -954,6 +954,10 @@ private: Expression Visit(const Node& node) { if (const auto operation = std::get_if<OperationNode>(&*node)) { + if (const auto amend_index = operation->GetAmendIndex()) { + [[maybe_unused]] const Type type = Visit(ir.GetAmendNode(*amend_index)).type; + ASSERT(type == Type::Void); + } const auto operation_index = static_cast<std::size_t>(operation->GetCode()); const auto decompiler = operation_decompilers[operation_index]; if (decompiler == nullptr) { @@ -1142,6 +1146,10 @@ private: } if (const auto conditional = std::get_if<ConditionalNode>(&*node)) { + if (const auto amend_index = conditional->GetAmendIndex()) { + [[maybe_unused]] const Type type = Visit(ir.GetAmendNode(*amend_index)).type; + ASSERT(type == Type::Void); + } // It's invalid to call conditional on nested nodes, use an operation instead const Id true_label = OpLabel(); const Id skip_label = OpLabel(); diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp new file mode 100644 index 000000000..171d78afc --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -0,0 +1,127 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <unordered_map> +#include <utility> +#include <vector> + +#include "common/bit_util.h" +#include "common/common_types.h" +#include "video_core/renderer_vulkan/vk_device.h" +#include "video_core/renderer_vulkan/vk_resource_manager.h" +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" + +namespace Vulkan { + +VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence, + u64 last_epoch) + : buffer{std::move(buffer)}, watch{fence}, last_epoch{last_epoch} {} + +VKStagingBufferPool::StagingBuffer::StagingBuffer(StagingBuffer&& rhs) noexcept { + buffer = std::move(rhs.buffer); + watch = std::move(rhs.watch); + last_epoch = rhs.last_epoch; +} + +VKStagingBufferPool::StagingBuffer::~StagingBuffer() = default; + +VKStagingBufferPool::StagingBuffer& VKStagingBufferPool::StagingBuffer::operator=( + StagingBuffer&& rhs) noexcept { + buffer = std::move(rhs.buffer); + watch = std::move(rhs.watch); + last_epoch = rhs.last_epoch; + return *this; +} + +VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device, VKMemoryManager& memory_manager, + VKScheduler& scheduler) + : device{device}, memory_manager{memory_manager}, scheduler{scheduler}, + is_device_integrated{device.IsIntegrated()} {} + +VKStagingBufferPool::~VKStagingBufferPool() = default; + +VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visible) { + if (const auto buffer = TryGetReservedBuffer(size, host_visible)) { + return *buffer; + } + return CreateStagingBuffer(size, host_visible); +} + +void VKStagingBufferPool::TickFrame() { + ++epoch; + current_delete_level = (current_delete_level + 1) % NumLevels; + + ReleaseCache(true); + if (!is_device_integrated) { + ReleaseCache(false); + } +} + +VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) { + for (auto& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) { + if (entry.watch.TryWatch(scheduler.GetFence())) { + entry.last_epoch = epoch; + return &*entry.buffer; + } + } + return nullptr; +} + +VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_visible) { + const auto usage = + vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst | + vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer; + const u32 log2 = Common::Log2Ceil64(size); + const vk::BufferCreateInfo buffer_ci({}, 1ULL << log2, usage, vk::SharingMode::eExclusive, 0, + nullptr); + const auto dev = device.GetLogical(); + auto buffer = std::make_unique<VKBuffer>(); + buffer->handle = dev.createBufferUnique(buffer_ci, nullptr, device.GetDispatchLoader()); + buffer->commit = memory_manager.Commit(*buffer->handle, host_visible); + + auto& entries = GetCache(host_visible)[log2].entries; + return *entries.emplace_back(std::move(buffer), scheduler.GetFence(), epoch).buffer; +} + +VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) { + return is_device_integrated || host_visible ? host_staging_buffers : device_staging_buffers; +} + +void VKStagingBufferPool::ReleaseCache(bool host_visible) { + auto& cache = GetCache(host_visible); + const u64 size = ReleaseLevel(cache, current_delete_level); + if (size == 0) { + return; + } +} + +u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t log2) { + static constexpr u64 epochs_to_destroy = 180; + static constexpr std::size_t deletions_per_tick = 16; + + auto& staging = cache[log2]; + auto& entries = staging.entries; + const std::size_t old_size = entries.size(); + + const auto is_deleteable = [this](const auto& entry) { + return entry.last_epoch + epochs_to_destroy < epoch && !entry.watch.IsUsed(); + }; + const std::size_t begin_offset = staging.delete_index; + const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size); + const auto begin = std::begin(entries) + begin_offset; + const auto end = std::begin(entries) + end_offset; + entries.erase(std::remove_if(begin, end, is_deleteable), end); + + const std::size_t new_size = entries.size(); + staging.delete_index += deletions_per_tick; + if (staging.delete_index >= new_size) { + staging.delete_index = 0; + } + + return (1ULL << log2) * (old_size - new_size); +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h new file mode 100644 index 000000000..02310375f --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h @@ -0,0 +1,83 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <climits> +#include <unordered_map> +#include <utility> +#include <vector> + +#include "common/common_types.h" + +#include "video_core/renderer_vulkan/declarations.h" +#include "video_core/renderer_vulkan/vk_memory_manager.h" + +namespace Vulkan { + +class VKDevice; +class VKFenceWatch; +class VKScheduler; + +struct VKBuffer final { + UniqueBuffer handle; + VKMemoryCommit commit; +}; + +class VKStagingBufferPool final { +public: + explicit VKStagingBufferPool(const VKDevice& device, VKMemoryManager& memory_manager, + VKScheduler& scheduler); + ~VKStagingBufferPool(); + + VKBuffer& GetUnusedBuffer(std::size_t size, bool host_visible); + + void TickFrame(); + +private: + struct StagingBuffer final { + explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence, u64 last_epoch); + StagingBuffer(StagingBuffer&& rhs) noexcept; + StagingBuffer(const StagingBuffer&) = delete; + ~StagingBuffer(); + + StagingBuffer& operator=(StagingBuffer&& rhs) noexcept; + + std::unique_ptr<VKBuffer> buffer; + VKFenceWatch watch; + u64 last_epoch = 0; + }; + + struct StagingBuffers final { + std::vector<StagingBuffer> entries; + std::size_t delete_index = 0; + }; + + static constexpr std::size_t NumLevels = sizeof(std::size_t) * CHAR_BIT; + using StagingBuffersCache = std::array<StagingBuffers, NumLevels>; + + VKBuffer* TryGetReservedBuffer(std::size_t size, bool host_visible); + + VKBuffer& CreateStagingBuffer(std::size_t size, bool host_visible); + + StagingBuffersCache& GetCache(bool host_visible); + + void ReleaseCache(bool host_visible); + + u64 ReleaseLevel(StagingBuffersCache& cache, std::size_t log2); + + const VKDevice& device; + VKMemoryManager& memory_manager; + VKScheduler& scheduler; + const bool is_device_integrated; + + StagingBuffersCache host_staging_buffers; + StagingBuffersCache device_staging_buffers; + + u64 epoch = 0; + + std::size_t current_delete_level = 0; +}; + +} // namespace Vulkan diff --git a/src/video_core/shader/decode/register_set_predicate.cpp b/src/video_core/shader/decode/register_set_predicate.cpp index e6c9d287e..8d54cce34 100644 --- a/src/video_core/shader/decode/register_set_predicate.cpp +++ b/src/video_core/shader/decode/register_set_predicate.cpp @@ -13,37 +13,65 @@ namespace VideoCommon::Shader { using Tegra::Shader::Instruction; using Tegra::Shader::OpCode; +namespace { +constexpr u64 NUM_PROGRAMMABLE_PREDICATES = 7; +} + u32 ShaderIR::DecodeRegisterSetPredicate(NodeBlock& bb, u32 pc) { const Instruction instr = {program_code[pc]}; const auto opcode = OpCode::Decode(instr); - UNIMPLEMENTED_IF(instr.r2p.mode != Tegra::Shader::R2pMode::Pr); + UNIMPLEMENTED_IF(instr.p2r_r2p.mode != Tegra::Shader::R2pMode::Pr); - const Node apply_mask = [&]() { + const Node apply_mask = [&] { switch (opcode->get().GetId()) { case OpCode::Id::R2P_IMM: - return Immediate(static_cast<u32>(instr.r2p.immediate_mask)); + case OpCode::Id::P2R_IMM: + return Immediate(static_cast<u32>(instr.p2r_r2p.immediate_mask)); default: UNREACHABLE(); - return Immediate(static_cast<u32>(instr.r2p.immediate_mask)); + return Immediate(0); } }(); - const Node mask = GetRegister(instr.gpr8); - const auto offset = static_cast<u32>(instr.r2p.byte) * 8; - constexpr u32 programmable_preds = 7; - for (u64 pred = 0; pred < programmable_preds; ++pred) { - const auto shift = static_cast<u32>(pred); + const auto offset = static_cast<u32>(instr.p2r_r2p.byte) * 8; + + switch (opcode->get().GetId()) { + case OpCode::Id::R2P_IMM: { + const Node mask = GetRegister(instr.gpr8); - const Node apply_compare = BitfieldExtract(apply_mask, shift, 1); - const Node condition = - Operation(OperationCode::LogicalUNotEqual, apply_compare, Immediate(0)); + for (u64 pred = 0; pred < NUM_PROGRAMMABLE_PREDICATES; ++pred) { + const auto shift = static_cast<u32>(pred); - const Node value_compare = BitfieldExtract(mask, offset + shift, 1); - const Node value = Operation(OperationCode::LogicalUNotEqual, value_compare, Immediate(0)); + const Node apply_compare = BitfieldExtract(apply_mask, shift, 1); + const Node condition = + Operation(OperationCode::LogicalUNotEqual, apply_compare, Immediate(0)); - const Node code = Operation(OperationCode::LogicalAssign, GetPredicate(pred), value); - bb.push_back(Conditional(condition, {code})); + const Node value_compare = BitfieldExtract(mask, offset + shift, 1); + const Node value = + Operation(OperationCode::LogicalUNotEqual, value_compare, Immediate(0)); + + const Node code = Operation(OperationCode::LogicalAssign, GetPredicate(pred), value); + bb.push_back(Conditional(condition, {code})); + } + break; + } + case OpCode::Id::P2R_IMM: { + Node value = Immediate(0); + for (u64 pred = 0; pred < NUM_PROGRAMMABLE_PREDICATES; ++pred) { + Node bit = Operation(OperationCode::Select, GetPredicate(pred), Immediate(1U << pred), + Immediate(0)); + value = Operation(OperationCode::UBitwiseOr, std::move(value), std::move(bit)); + } + value = Operation(OperationCode::UBitwiseAnd, std::move(value), apply_mask); + value = BitfieldInsert(GetRegister(instr.gpr8), std::move(value), offset, 8); + + SetRegister(bb, instr.gpr0, std::move(value)); + break; + } + default: + UNIMPLEMENTED_MSG("Unhandled P2R/R2R instruction: {}", opcode->get().GetName()); + break; } return pc; diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index 4d2f4d6a8..4e155542a 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h @@ -392,8 +392,30 @@ struct MetaImage { using Meta = std::variant<MetaArithmetic, MetaTexture, MetaImage, MetaStackClass, Tegra::Shader::HalfType>; +class AmendNode { +public: + std::optional<std::size_t> GetAmendIndex() const { + if (amend_index == amend_null_index) { + return std::nullopt; + } + return {amend_index}; + } + + void SetAmendIndex(std::size_t index) { + amend_index = index; + } + + void ClearAmend() { + amend_index = amend_null_index; + } + +private: + static constexpr std::size_t amend_null_index = 0xFFFFFFFFFFFFFFFFULL; + std::size_t amend_index{amend_null_index}; +}; + /// Holds any kind of operation that can be done in the IR -class OperationNode final { +class OperationNode final : public AmendNode { public: explicit OperationNode(OperationCode code) : OperationNode(code, Meta{}) {} @@ -433,7 +455,7 @@ private: }; /// Encloses inside any kind of node that returns a boolean conditionally-executed code -class ConditionalNode final { +class ConditionalNode final : public AmendNode { public: explicit ConditionalNode(Node condition, std::vector<Node>&& code) : condition{std::move(condition)}, code{std::move(code)} {} diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index 1d9825c76..31eecb3f4 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp @@ -446,4 +446,10 @@ Node ShaderIR::BitfieldInsert(Node base, Node insert, u32 offset, u32 bits) { Immediate(bits)); } +std::size_t ShaderIR::DeclareAmend(Node new_amend) { + const std::size_t id = amend_code.size(); + amend_code.push_back(new_amend); + return id; +} + } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index baed06ccd..aacd0a0da 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -176,6 +176,10 @@ public: /// Returns a condition code evaluated from internal flags Node GetConditionCode(Tegra::Shader::ConditionCode cc) const; + const Node& GetAmendNode(std::size_t index) const { + return amend_code[index]; + } + private: friend class ASTDecoder; @@ -392,6 +396,9 @@ private: Tegra::Shader::Instruction instr, bool is_write); + /// Register new amending code and obtain the reference id. + std::size_t DeclareAmend(Node new_amend); + const ProgramCode& program_code; const u32 main_offset; const CompilerSettings settings; @@ -406,6 +413,7 @@ private: std::map<u32, NodeBlock> basic_blocks; NodeBlock global_code; ASTManager program_manager{true, true}; + std::vector<Node> amend_code; std::set<u32> used_registers; std::set<Tegra::Shader::Pred> used_predicates; diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index ff1c1d985..11ae1e66e 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -78,11 +78,6 @@ add_executable(yuzu configuration/configure_web.cpp configuration/configure_web.h configuration/configure_web.ui - debugger/graphics/graphics_breakpoint_observer.cpp - debugger/graphics/graphics_breakpoint_observer.h - debugger/graphics/graphics_breakpoints.cpp - debugger/graphics/graphics_breakpoints.h - debugger/graphics/graphics_breakpoints_p.h debugger/console.cpp debugger/console.h debugger/profiler.cpp diff --git a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp deleted file mode 100644 index 5f459ccfb..000000000 --- a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <QMetaType> -#include "yuzu/debugger/graphics/graphics_breakpoint_observer.h" - -BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Tegra::DebugContext> debug_context, - const QString& title, QWidget* parent) - : QDockWidget(title, parent), BreakPointObserver(debug_context) { - qRegisterMetaType<Tegra::DebugContext::Event>("Tegra::DebugContext::Event"); - - connect(this, &BreakPointObserverDock::Resumed, this, &BreakPointObserverDock::OnResumed); - - // NOTE: This signal is emitted from a non-GUI thread, but connect() takes - // care of delaying its handling to the GUI thread. - connect(this, &BreakPointObserverDock::BreakPointHit, this, - &BreakPointObserverDock::OnBreakPointHit, Qt::BlockingQueuedConnection); -} - -void BreakPointObserverDock::OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) { - emit BreakPointHit(event, data); -} - -void BreakPointObserverDock::OnMaxwellResume() { - emit Resumed(); -} diff --git a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h deleted file mode 100644 index ab32f0115..000000000 --- a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <QDockWidget> -#include "video_core/debug_utils/debug_utils.h" - -/** - * Utility class which forwards calls to OnMaxwellBreakPointHit and OnMaxwellResume to public slots. - * This is because the Maxwell breakpoint callbacks are called from a non-GUI thread, while - * the widget usually wants to perform reactions in the GUI thread. - */ -class BreakPointObserverDock : public QDockWidget, - protected Tegra::DebugContext::BreakPointObserver { - Q_OBJECT - -public: - BreakPointObserverDock(std::shared_ptr<Tegra::DebugContext> debug_context, const QString& title, - QWidget* parent = nullptr); - - void OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) override; - void OnMaxwellResume() override; - -signals: - void Resumed(); - void BreakPointHit(Tegra::DebugContext::Event event, void* data); - -private: - virtual void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) = 0; - virtual void OnResumed() = 0; -}; diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp deleted file mode 100644 index 1c80082a4..000000000 --- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <QLabel> -#include <QMetaType> -#include <QPushButton> -#include <QTreeView> -#include <QVBoxLayout> -#include "common/assert.h" -#include "yuzu/debugger/graphics/graphics_breakpoints.h" -#include "yuzu/debugger/graphics/graphics_breakpoints_p.h" - -BreakPointModel::BreakPointModel(std::shared_ptr<Tegra::DebugContext> debug_context, - QObject* parent) - : QAbstractListModel(parent), context_weak(debug_context), - at_breakpoint(debug_context->at_breakpoint), - active_breakpoint(debug_context->active_breakpoint) {} - -int BreakPointModel::columnCount(const QModelIndex& parent) const { - return 1; -} - -int BreakPointModel::rowCount(const QModelIndex& parent) const { - return static_cast<int>(Tegra::DebugContext::Event::NumEvents); -} - -QVariant BreakPointModel::data(const QModelIndex& index, int role) const { - const auto event = static_cast<Tegra::DebugContext::Event>(index.row()); - - switch (role) { - case Qt::DisplayRole: { - if (index.column() == 0) { - return DebugContextEventToString(event); - } - break; - } - - case Qt::CheckStateRole: { - if (index.column() == 0) - return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked; - break; - } - - case Qt::BackgroundRole: { - if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) { - return QBrush(QColor(0xE0, 0xE0, 0x10)); - } - break; - } - - case Role_IsEnabled: { - auto context = context_weak.lock(); - return context && context->breakpoints[(int)event].enabled; - } - - default: - break; - } - return QVariant(); -} - -Qt::ItemFlags BreakPointModel::flags(const QModelIndex& index) const { - if (!index.isValid()) - return 0; - - Qt::ItemFlags flags = Qt::ItemIsEnabled; - if (index.column() == 0) - flags |= Qt::ItemIsUserCheckable; - return flags; -} - -bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) { - const auto event = static_cast<Tegra::DebugContext::Event>(index.row()); - - switch (role) { - case Qt::CheckStateRole: { - if (index.column() != 0) - return false; - - auto context = context_weak.lock(); - if (!context) - return false; - - context->breakpoints[(int)event].enabled = value == Qt::Checked; - QModelIndex changed_index = createIndex(index.row(), 0); - emit dataChanged(changed_index, changed_index); - return true; - } - } - - return false; -} - -void BreakPointModel::OnBreakPointHit(Tegra::DebugContext::Event event) { - auto context = context_weak.lock(); - if (!context) - return; - - active_breakpoint = context->active_breakpoint; - at_breakpoint = context->at_breakpoint; - emit dataChanged(createIndex(static_cast<int>(event), 0), - createIndex(static_cast<int>(event), 0)); -} - -void BreakPointModel::OnResumed() { - auto context = context_weak.lock(); - if (!context) - return; - - at_breakpoint = context->at_breakpoint; - emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0), - createIndex(static_cast<int>(active_breakpoint), 0)); - active_breakpoint = context->active_breakpoint; -} - -QString BreakPointModel::DebugContextEventToString(Tegra::DebugContext::Event event) { - switch (event) { - case Tegra::DebugContext::Event::MaxwellCommandLoaded: - return tr("Maxwell command loaded"); - case Tegra::DebugContext::Event::MaxwellCommandProcessed: - return tr("Maxwell command processed"); - case Tegra::DebugContext::Event::IncomingPrimitiveBatch: - return tr("Incoming primitive batch"); - case Tegra::DebugContext::Event::FinishedPrimitiveBatch: - return tr("Finished primitive batch"); - case Tegra::DebugContext::Event::NumEvents: - break; - } - - return tr("Unknown debug context event"); -} - -GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( - std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent) - : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver( - debug_context) { - setObjectName(QStringLiteral("TegraBreakPointsWidget")); - - status_text = new QLabel(tr("Emulation running")); - resume_button = new QPushButton(tr("Resume")); - resume_button->setEnabled(false); - - breakpoint_model = new BreakPointModel(debug_context, this); - breakpoint_list = new QTreeView; - breakpoint_list->setRootIsDecorated(false); - breakpoint_list->setHeaderHidden(true); - breakpoint_list->setModel(breakpoint_model); - - qRegisterMetaType<Tegra::DebugContext::Event>("Tegra::DebugContext::Event"); - - connect(breakpoint_list, &QTreeView::doubleClicked, this, - &GraphicsBreakPointsWidget::OnItemDoubleClicked); - - connect(resume_button, &QPushButton::clicked, this, - &GraphicsBreakPointsWidget::OnResumeRequested); - - connect(this, &GraphicsBreakPointsWidget::BreakPointHit, this, - &GraphicsBreakPointsWidget::OnBreakPointHit, Qt::BlockingQueuedConnection); - connect(this, &GraphicsBreakPointsWidget::Resumed, this, &GraphicsBreakPointsWidget::OnResumed); - - connect(this, &GraphicsBreakPointsWidget::BreakPointHit, breakpoint_model, - &BreakPointModel::OnBreakPointHit, Qt::BlockingQueuedConnection); - connect(this, &GraphicsBreakPointsWidget::Resumed, breakpoint_model, - &BreakPointModel::OnResumed); - - connect(this, &GraphicsBreakPointsWidget::BreakPointsChanged, - [this](const QModelIndex& top_left, const QModelIndex& bottom_right) { - breakpoint_model->dataChanged(top_left, bottom_right); - }); - - QWidget* main_widget = new QWidget; - auto main_layout = new QVBoxLayout; - { - auto sub_layout = new QHBoxLayout; - sub_layout->addWidget(status_text); - sub_layout->addWidget(resume_button); - main_layout->addLayout(sub_layout); - } - main_layout->addWidget(breakpoint_list); - main_widget->setLayout(main_layout); - - setWidget(main_widget); -} - -void GraphicsBreakPointsWidget::OnMaxwellBreakPointHit(Event event, void* data) { - // Process in GUI thread - emit BreakPointHit(event, data); -} - -void GraphicsBreakPointsWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) { - status_text->setText(tr("Emulation halted at breakpoint")); - resume_button->setEnabled(true); -} - -void GraphicsBreakPointsWidget::OnMaxwellResume() { - // Process in GUI thread - emit Resumed(); -} - -void GraphicsBreakPointsWidget::OnResumed() { - status_text->setText(tr("Emulation running")); - resume_button->setEnabled(false); -} - -void GraphicsBreakPointsWidget::OnResumeRequested() { - if (auto context = context_weak.lock()) - context->Resume(); -} - -void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) { - if (!index.isValid()) - return; - - QModelIndex check_index = breakpoint_list->model()->index(index.row(), 0); - QVariant enabled = breakpoint_list->model()->data(check_index, Qt::CheckStateRole); - QVariant new_state = Qt::Unchecked; - if (enabled == Qt::Unchecked) - new_state = Qt::Checked; - breakpoint_list->model()->setData(check_index, new_state, Qt::CheckStateRole); -} diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.h b/src/yuzu/debugger/graphics/graphics_breakpoints.h deleted file mode 100644 index a920a2ae5..000000000 --- a/src/yuzu/debugger/graphics/graphics_breakpoints.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <memory> -#include <QDockWidget> -#include "video_core/debug_utils/debug_utils.h" - -class QLabel; -class QPushButton; -class QTreeView; - -class BreakPointModel; - -class GraphicsBreakPointsWidget : public QDockWidget, Tegra::DebugContext::BreakPointObserver { - Q_OBJECT - - using Event = Tegra::DebugContext::Event; - -public: - explicit GraphicsBreakPointsWidget(std::shared_ptr<Tegra::DebugContext> debug_context, - QWidget* parent = nullptr); - - void OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) override; - void OnMaxwellResume() override; - -signals: - void Resumed(); - void BreakPointHit(Tegra::DebugContext::Event event, void* data); - void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); - -private: - void OnBreakPointHit(Tegra::DebugContext::Event event, void* data); - void OnItemDoubleClicked(const QModelIndex&); - void OnResumeRequested(); - void OnResumed(); - - QLabel* status_text; - QPushButton* resume_button; - - BreakPointModel* breakpoint_model; - QTreeView* breakpoint_list; -}; diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h deleted file mode 100644 index fb488e38f..000000000 --- a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <memory> -#include <QAbstractListModel> -#include "video_core/debug_utils/debug_utils.h" - -class BreakPointModel : public QAbstractListModel { - Q_OBJECT - -public: - enum { - Role_IsEnabled = Qt::UserRole, - }; - - BreakPointModel(std::shared_ptr<Tegra::DebugContext> context, QObject* parent); - - int columnCount(const QModelIndex& parent = QModelIndex()) const override; - int rowCount(const QModelIndex& parent = QModelIndex()) const override; - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - Qt::ItemFlags flags(const QModelIndex& index) const override; - - bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; - - void OnBreakPointHit(Tegra::DebugContext::Event event); - void OnResumed(); - -private: - static QString DebugContextEventToString(Tegra::DebugContext::Event event); - - std::weak_ptr<Tegra::DebugContext> context_weak; - bool at_breakpoint; - Tegra::DebugContext::Event active_breakpoint; -}; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 867f8e913..b21fbf826 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -93,7 +93,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/perf_stats.h" #include "core/settings.h" #include "core/telemetry_session.h" -#include "video_core/debug_utils/debug_utils.h" #include "yuzu/about_dialog.h" #include "yuzu/bootmanager.h" #include "yuzu/compatdb.h" @@ -101,7 +100,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_dialog.h" #include "yuzu/debugger/console.h" -#include "yuzu/debugger/graphics/graphics_breakpoints.h" #include "yuzu/debugger/profiler.h" #include "yuzu/debugger/wait_tree.h" #include "yuzu/discord.h" @@ -187,8 +185,6 @@ GMainWindow::GMainWindow() provider(std::make_unique<FileSys::ManualContentProvider>()) { InitializeLogging(); - debug_context = Tegra::DebugContext::Construct(); - setAcceptDrops(true); ui.setupUi(this); statusBar()->hide(); @@ -495,11 +491,6 @@ void GMainWindow::InitializeDebugWidgets() { debug_menu->addAction(microProfileDialog->toggleViewAction()); #endif - graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(debug_context, this); - addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget); - graphicsBreakpointsWidget->hide(); - debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); - waitTreeWidget = new WaitTreeWidget(this); addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); waitTreeWidget->hide(); @@ -869,8 +860,6 @@ bool GMainWindow::LoadROM(const QString& filename) { Core::System& system{Core::System::GetInstance()}; system.SetFilesystem(vfs); - system.SetGPUDebugContext(debug_context); - system.SetAppletFrontendSet({ nullptr, // Parental Controls std::make_unique<QtErrorDisplay>(*this), // diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 7f46bea2b..a56f9a981 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -22,7 +22,6 @@ class Config; class EmuThread; class GameList; class GImageInfo; -class GraphicsBreakPointsWidget; class GRenderWindow; class LoadingScreen; class MicroProfileDialog; @@ -42,10 +41,6 @@ class ManualContentProvider; class VfsFilesystem; } // namespace FileSys -namespace Tegra { -class DebugContext; -} - enum class EmulatedDirectoryTarget { NAND, SDMC, @@ -223,8 +218,6 @@ private: Ui::MainWindow ui; - std::shared_ptr<Tegra::DebugContext> debug_context; - GRenderWindow* render_window; GameList* game_list; LoadingScreen* loading_screen; @@ -255,7 +248,6 @@ private: // Debugger panes ProfilerWidget* profilerWidget; MicroProfileDialog* microProfileDialog; - GraphicsBreakPointsWidget* graphicsBreakpointsWidget; WaitTreeWidget* waitTreeWidget; QAction* actions_recent_files[max_recent_files_item]; |