diff options
Diffstat (limited to 'src/video_core')
-rw-r--r-- | src/video_core/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/video_core/debug_utils/debug_utils.cpp | 49 | ||||
-rw-r--r-- | src/video_core/debug_utils/debug_utils.h | 157 | ||||
-rw-r--r-- | src/video_core/engines/maxwell_3d.cpp | 31 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 6 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/maxwell_to_gl.h | 22 | ||||
-rw-r--r-- | src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | 8 | ||||
-rw-r--r-- | src/video_core/shader/node.h | 26 | ||||
-rw-r--r-- | src/video_core/shader/shader_ir.cpp | 6 | ||||
-rw-r--r-- | src/video_core/shader/shader_ir.h | 8 |
10 files changed, 70 insertions, 245 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 13f9848bc..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 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/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_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/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; |