diff options
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/frontend/emu_window.h | 41 | ||||
| -rw-r--r-- | src/core/hle/service/nvflinger/buffer_queue.cpp | 48 | ||||
| -rw-r--r-- | src/core/hle/service/nvflinger/buffer_queue.h | 2 | ||||
| -rw-r--r-- | src/core/hle/service/vi/vi.cpp | 46 | ||||
| -rw-r--r-- | src/core/memory.cpp | 127 | ||||
| -rw-r--r-- | src/core/memory.h | 78 | 
6 files changed, 312 insertions, 30 deletions
| diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 72294d4d8..13aa14934 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -12,6 +12,15 @@  namespace Core::Frontend { +/// Information for the Graphics Backends signifying what type of screen pointer is in +/// WindowInformation +enum class WindowSystemType { +    Headless, +    Windows, +    X11, +    Wayland, +}; +  /**   * Represents a drawing context that supports graphics operations.   */ @@ -76,6 +85,23 @@ public:          std::pair<unsigned, unsigned> min_client_area_size;      }; +    /// Data describing host window system information +    struct WindowSystemInfo { +        // Window system type. Determines which GL context or Vulkan WSI is used. +        WindowSystemType type = WindowSystemType::Headless; + +        // Connection to a display server. This is used on X11 and Wayland platforms. +        void* display_connection = nullptr; + +        // Render surface. This is a pointer to the native window handle, which depends +        // on the platform. e.g. HWND for Windows, Window for X11. If the surface is +        // set to nullptr, the video backend will run in headless mode. +        void* render_surface = nullptr; + +        // Scale of the render surface. For hidpi systems, this will be >1. +        float render_surface_scale = 1.0f; +    }; +      /// Polls window events      virtual void PollEvents() = 0; @@ -87,10 +113,6 @@ public:      /// Returns if window is shown (not minimized)      virtual bool IsShown() const = 0; -    /// Retrieves Vulkan specific handlers from the window -    virtual void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, -                                        void* surface) const = 0; -      /**       * Signal that a touch pressed event has occurred (e.g. mouse click pressed)       * @param framebuffer_x Framebuffer x-coordinate that was pressed @@ -128,6 +150,13 @@ public:      }      /** +     * Returns system information about the drawing area. +     */ +    const WindowSystemInfo& GetWindowInfo() const { +        return window_info; +    } + +    /**       * Gets the framebuffer layout (width, height, and screen regions)       * @note This method is thread-safe       */ @@ -142,7 +171,7 @@ public:      void UpdateCurrentFramebufferLayout(unsigned width, unsigned height);  protected: -    EmuWindow(); +    explicit EmuWindow();      virtual ~EmuWindow();      /** @@ -179,6 +208,8 @@ protected:          client_area_height = size.second;      } +    WindowSystemInfo window_info; +  private:      /**       * Handler called when the minimal client area was requested to be changed via SetConfig. diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 32b6f4b27..f1e3d832a 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -28,6 +28,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)      buffer.slot = slot;      buffer.igbp_buffer = igbp_buffer;      buffer.status = Buffer::Status::Free; +    free_buffers.push_back(slot);      queue.emplace_back(buffer);      buffer_wait_event.writable->Signal(); @@ -35,16 +36,37 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)  std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,                                                                                         u32 height) { -    auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { -        // Only consider free buffers. Buffers become free once again after they've been Acquired -        // and Released by the compositor, see the NVFlinger::Compose method. -        if (buffer.status != Buffer::Status::Free) { -            return false; -        } -        // Make sure that the parameters match. -        return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height; -    }); +    if (free_buffers.empty()) { +        return {}; +    } + +    auto f_itr = free_buffers.begin(); +    auto itr = queue.end(); + +    while (f_itr != free_buffers.end()) { +        auto slot = *f_itr; +        itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { +            // Only consider free buffers. Buffers become free once again after they've been +            // Acquired and Released by the compositor, see the NVFlinger::Compose method. +            if (buffer.status != Buffer::Status::Free) { +                return false; +            } + +            if (buffer.slot != slot) { +                return false; +            } + +            // Make sure that the parameters match. +            return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height; +        }); + +        if (itr != queue.end()) { +            free_buffers.erase(f_itr); +            break; +        } +        ++f_itr; +    }      if (itr == queue.end()) {          return {}; @@ -99,10 +121,18 @@ void BufferQueue::ReleaseBuffer(u32 slot) {      ASSERT(itr != queue.end());      ASSERT(itr->status == Buffer::Status::Acquired);      itr->status = Buffer::Status::Free; +    free_buffers.push_back(slot);      buffer_wait_event.writable->Signal();  } +void BufferQueue::Disconnect() { +    queue.clear(); +    queue_sequence.clear(); +    id = 1; +    layer_id = 1; +} +  u32 BufferQueue::Query(QueryType type) {      LOG_WARNING(Service, "(STUBBED) called type={}", static_cast<u32>(type)); diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index f4bbfd945..d5f31e567 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -87,6 +87,7 @@ public:                       Service::Nvidia::MultiFence& multi_fence);      std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();      void ReleaseBuffer(u32 slot); +    void Disconnect();      u32 Query(QueryType type);      u32 GetId() const { @@ -101,6 +102,7 @@ private:      u32 id;      u64 layer_id; +    std::list<u32> free_buffers;      std::vector<Buffer> queue;      std::list<u32> queue_sequence;      Kernel::EventPair buffer_wait_event; diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 519da74e0..fdc62d05b 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -513,7 +513,8 @@ private:          auto& buffer_queue = nv_flinger->FindBufferQueue(id); -        if (transaction == TransactionId::Connect) { +        switch (transaction) { +        case TransactionId::Connect: {              IGBPConnectRequestParcel request{ctx.ReadBuffer()};              IGBPConnectResponseParcel response{                  static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) * @@ -521,14 +522,18 @@ private:                  static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *                                   Settings::values.resolution_factor)};              ctx.WriteBuffer(response.Serialize()); -        } else if (transaction == TransactionId::SetPreallocatedBuffer) { +            break; +        } +        case TransactionId::SetPreallocatedBuffer: {              IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};              buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer);              IGBPSetPreallocatedBufferResponseParcel response{};              ctx.WriteBuffer(response.Serialize()); -        } else if (transaction == TransactionId::DequeueBuffer) { +            break; +        } +        case TransactionId::DequeueBuffer: {              IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};              const u32 width{request.data.width};              const u32 height{request.data.height}; @@ -556,14 +561,18 @@ private:                      },                      buffer_queue.GetWritableBufferWaitEvent());              } -        } else if (transaction == TransactionId::RequestBuffer) { +            break; +        } +        case TransactionId::RequestBuffer: {              IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};              auto& buffer = buffer_queue.RequestBuffer(request.slot);              IGBPRequestBufferResponseParcel response{buffer};              ctx.WriteBuffer(response.Serialize()); -        } else if (transaction == TransactionId::QueueBuffer) { +            break; +        } +        case TransactionId::QueueBuffer: {              IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};              buffer_queue.QueueBuffer(request.data.slot, request.data.transform, @@ -572,7 +581,9 @@ private:              IGBPQueueBufferResponseParcel response{1280, 720};              ctx.WriteBuffer(response.Serialize()); -        } else if (transaction == TransactionId::Query) { +            break; +        } +        case TransactionId::Query: {              IGBPQueryRequestParcel request{ctx.ReadBuffer()};              const u32 value = @@ -580,15 +591,30 @@ private:              IGBPQueryResponseParcel response{value};              ctx.WriteBuffer(response.Serialize()); -        } else if (transaction == TransactionId::CancelBuffer) { +            break; +        } +        case TransactionId::CancelBuffer: {              LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); -        } else if (transaction == TransactionId::Disconnect || -                   transaction == TransactionId::DetachBuffer) { +            break; +        } +        case TransactionId::Disconnect: { +            LOG_WARNING(Service_VI, "(STUBBED) called, transaction=Disconnect"); +            const auto buffer = ctx.ReadBuffer(); + +            buffer_queue.Disconnect(); + +            IGBPEmptyResponseParcel response{}; +            ctx.WriteBuffer(response.Serialize()); +            break; +        } +        case TransactionId::DetachBuffer: {              const auto buffer = ctx.ReadBuffer();              IGBPEmptyResponseParcel response{};              ctx.WriteBuffer(response.Serialize()); -        } else { +            break; +        } +        default:              ASSERT_MSG(false, "Unimplemented");          } diff --git a/src/core/memory.cpp b/src/core/memory.cpp index f0888327f..6061d37ae 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -242,7 +242,52 @@ struct Memory::Impl {              }              case Common::PageType::RasterizerCachedMemory: {                  const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); -                system.GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount); +                system.GPU().FlushRegion(current_vaddr, copy_amount); +                std::memcpy(dest_buffer, host_ptr, copy_amount); +                break; +            } +            default: +                UNREACHABLE(); +            } + +            page_index++; +            page_offset = 0; +            dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; +            remaining_size -= copy_amount; +        } +    } + +    void ReadBlockUnsafe(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, +                         const std::size_t size) { +        const auto& page_table = process.VMManager().page_table; + +        std::size_t remaining_size = size; +        std::size_t page_index = src_addr >> PAGE_BITS; +        std::size_t page_offset = src_addr & PAGE_MASK; + +        while (remaining_size > 0) { +            const std::size_t copy_amount = +                std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); +            const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); + +            switch (page_table.attributes[page_index]) { +            case Common::PageType::Unmapped: { +                LOG_ERROR(HW_Memory, +                          "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", +                          current_vaddr, src_addr, size); +                std::memset(dest_buffer, 0, copy_amount); +                break; +            } +            case Common::PageType::Memory: { +                DEBUG_ASSERT(page_table.pointers[page_index]); + +                const u8* const src_ptr = +                    page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); +                std::memcpy(dest_buffer, src_ptr, copy_amount); +                break; +            } +            case Common::PageType::RasterizerCachedMemory: { +                const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr);                  std::memcpy(dest_buffer, host_ptr, copy_amount);                  break;              } @@ -261,6 +306,10 @@ struct Memory::Impl {          ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size);      } +    void ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) { +        ReadBlockUnsafe(*system.CurrentProcess(), src_addr, dest_buffer, size); +    } +      void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,                      const std::size_t size) {          const auto& page_table = process.VMManager().page_table; @@ -290,7 +339,50 @@ struct Memory::Impl {              }              case Common::PageType::RasterizerCachedMemory: {                  u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); -                system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount); +                system.GPU().InvalidateRegion(current_vaddr, copy_amount); +                std::memcpy(host_ptr, src_buffer, copy_amount); +                break; +            } +            default: +                UNREACHABLE(); +            } + +            page_index++; +            page_offset = 0; +            src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; +            remaining_size -= copy_amount; +        } +    } + +    void WriteBlockUnsafe(const Kernel::Process& process, const VAddr dest_addr, +                          const void* src_buffer, const std::size_t size) { +        const auto& page_table = process.VMManager().page_table; +        std::size_t remaining_size = size; +        std::size_t page_index = dest_addr >> PAGE_BITS; +        std::size_t page_offset = dest_addr & PAGE_MASK; + +        while (remaining_size > 0) { +            const std::size_t copy_amount = +                std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); +            const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); + +            switch (page_table.attributes[page_index]) { +            case Common::PageType::Unmapped: { +                LOG_ERROR(HW_Memory, +                          "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", +                          current_vaddr, dest_addr, size); +                break; +            } +            case Common::PageType::Memory: { +                DEBUG_ASSERT(page_table.pointers[page_index]); + +                u8* const dest_ptr = +                    page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); +                std::memcpy(dest_ptr, src_buffer, copy_amount); +                break; +            } +            case Common::PageType::RasterizerCachedMemory: { +                u8* const host_ptr = GetPointerFromVMA(process, current_vaddr);                  std::memcpy(host_ptr, src_buffer, copy_amount);                  break;              } @@ -309,6 +401,10 @@ struct Memory::Impl {          WriteBlock(*system.CurrentProcess(), dest_addr, src_buffer, size);      } +    void WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { +        WriteBlockUnsafe(*system.CurrentProcess(), dest_addr, src_buffer, size); +    } +      void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) {          const auto& page_table = process.VMManager().page_table;          std::size_t remaining_size = size; @@ -337,7 +433,7 @@ struct Memory::Impl {              }              case Common::PageType::RasterizerCachedMemory: {                  u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); -                system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount); +                system.GPU().InvalidateRegion(current_vaddr, copy_amount);                  std::memset(host_ptr, 0, copy_amount);                  break;              } @@ -384,7 +480,7 @@ struct Memory::Impl {              }              case Common::PageType::RasterizerCachedMemory: {                  const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); -                system.GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount); +                system.GPU().FlushRegion(current_vaddr, copy_amount);                  WriteBlock(process, dest_addr, host_ptr, copy_amount);                  break;              } @@ -545,7 +641,7 @@ struct Memory::Impl {              break;          case Common::PageType::RasterizerCachedMemory: {              const u8* const host_ptr = GetPointerFromVMA(vaddr); -            system.GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T)); +            system.GPU().FlushRegion(vaddr, sizeof(T));              T value;              std::memcpy(&value, host_ptr, sizeof(T));              return value; @@ -587,7 +683,7 @@ struct Memory::Impl {              break;          case Common::PageType::RasterizerCachedMemory: {              u8* const host_ptr{GetPointerFromVMA(vaddr)}; -            system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T)); +            system.GPU().InvalidateRegion(vaddr, sizeof(T));              std::memcpy(host_ptr, &data, sizeof(T));              break;          } @@ -696,6 +792,15 @@ void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_      impl->ReadBlock(src_addr, dest_buffer, size);  } +void Memory::ReadBlockUnsafe(const Kernel::Process& process, const VAddr src_addr, +                             void* dest_buffer, const std::size_t size) { +    impl->ReadBlockUnsafe(process, src_addr, dest_buffer, size); +} + +void Memory::ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) { +    impl->ReadBlockUnsafe(src_addr, dest_buffer, size); +} +  void Memory::WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,                          std::size_t size) {      impl->WriteBlock(process, dest_addr, src_buffer, size); @@ -705,6 +810,16 @@ void Memory::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std      impl->WriteBlock(dest_addr, src_buffer, size);  } +void Memory::WriteBlockUnsafe(const Kernel::Process& process, VAddr dest_addr, +                              const void* src_buffer, std::size_t size) { +    impl->WriteBlockUnsafe(process, dest_addr, src_buffer, size); +} + +void Memory::WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, +                              const std::size_t size) { +    impl->WriteBlockUnsafe(dest_addr, src_buffer, size); +} +  void Memory::ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size) {      impl->ZeroBlock(process, dest_addr, size);  } diff --git a/src/core/memory.h b/src/core/memory.h index 8913a9da4..b92d678a4 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -295,6 +295,27 @@ public:                     std::size_t size);      /** +     * Reads a contiguous block of bytes from a specified process' address space. +     * This unsafe version does not trigger GPU flushing. +     * +     * @param process     The process to read the data from. +     * @param src_addr    The virtual address to begin reading from. +     * @param dest_buffer The buffer to place the read bytes into. +     * @param size        The amount of data to read, in bytes. +     * +     * @note If a size of 0 is specified, then this function reads nothing and +     *       no attempts to access memory are made at all. +     * +     * @pre dest_buffer must be at least size bytes in length, otherwise a +     *      buffer overrun will occur. +     * +     * @post The range [dest_buffer, size) contains the read bytes from the +     *       process' address space. +     */ +    void ReadBlockUnsafe(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, +                         std::size_t size); + +    /**       * Reads a contiguous block of bytes from the current process' address space.       *       * @param src_addr    The virtual address to begin reading from. @@ -313,6 +334,25 @@ public:      void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size);      /** +     * Reads a contiguous block of bytes from the current process' address space. +     * This unsafe version does not trigger GPU flushing. +     * +     * @param src_addr    The virtual address to begin reading from. +     * @param dest_buffer The buffer to place the read bytes into. +     * @param size        The amount of data to read, in bytes. +     * +     * @note If a size of 0 is specified, then this function reads nothing and +     *       no attempts to access memory are made at all. +     * +     * @pre dest_buffer must be at least size bytes in length, otherwise a +     *      buffer overrun will occur. +     * +     * @post The range [dest_buffer, size) contains the read bytes from the +     *       current process' address space. +     */ +    void ReadBlockUnsafe(VAddr src_addr, void* dest_buffer, std::size_t size); + +    /**       * Writes a range of bytes into a given process' address space at the specified       * virtual address.       * @@ -336,6 +376,26 @@ public:                      std::size_t size);      /** +     * Writes a range of bytes into a given process' address space at the specified +     * virtual address. +     * This unsafe version does not invalidate GPU Memory. +     * +     * @param process    The process to write data into the address space of. +     * @param dest_addr  The destination virtual address to begin writing the data at. +     * @param src_buffer The data to write into the process' address space. +     * @param size       The size of the data to write, in bytes. +     * +     * @post The address range [dest_addr, size) in the process' address space +     *       contains the data that was within src_buffer. +     * +     * @post If an attempt is made to write into an unmapped region of memory, the writes +     *       will be ignored and an error will be logged. +     * +     */ +    void WriteBlockUnsafe(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, +                          std::size_t size); + +    /**       * Writes a range of bytes into the current process' address space at the specified       * virtual address.       * @@ -357,6 +417,24 @@ public:      void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size);      /** +     * Writes a range of bytes into the current process' address space at the specified +     * virtual address. +     * This unsafe version does not invalidate GPU Memory. +     * +     * @param dest_addr  The destination virtual address to begin writing the data at. +     * @param src_buffer The data to write into the current process' address space. +     * @param size       The size of the data to write, in bytes. +     * +     * @post The address range [dest_addr, size) in the current process' address space +     *       contains the data that was within src_buffer. +     * +     * @post If an attempt is made to write into an unmapped region of memory, the writes +     *       will be ignored and an error will be logged. +     * +     */ +    void WriteBlockUnsafe(VAddr dest_addr, const void* src_buffer, std::size_t size); + +    /**       * Fills the specified address range within a process' address space with zeroes.       *       * @param process   The process that will have a portion of its memory zeroed out. | 
