// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include "common/common_types.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_stream_buffer.h" namespace OpenGL { class BufferCacheRuntime; class Buffer : public VideoCommon::BufferBase { public: explicit Buffer(BufferCacheRuntime&, VideoCore::RasterizerInterface& rasterizer, VAddr cpu_addr, u64 size_bytes); explicit Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams); void ImmediateUpload(size_t offset, std::span data) noexcept; void ImmediateDownload(size_t offset, std::span data) noexcept; void MakeResident(GLenum access) noexcept; [[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format); [[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept { return address; } [[nodiscard]] GLuint Handle() const noexcept { return buffer.handle; } private: struct BufferView { u32 offset; u32 size; VideoCore::Surface::PixelFormat format; OGLTexture texture; }; GLuint64EXT address = 0; OGLBuffer buffer; GLenum current_residency_access = GL_NONE; std::vector views; }; class BufferCacheRuntime { friend Buffer; public: static constexpr u8 INVALID_BINDING = std::numeric_limits::max(); explicit BufferCacheRuntime(const Device& device_); void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, std::span copies); void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value); void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride); void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size); void BindComputeUniformBuffer(u32 binding_index, Buffer& buffer, u32 offset, u32 size); void BindStorageBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size, bool is_written); void BindComputeStorageBuffer(u32 binding_index, Buffer& buffer, u32 offset, u32 size, bool is_written); void BindTransformFeedbackBuffer(u32 index, Buffer& buffer, u32 offset, u32 size); void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size, VideoCore::Surface::PixelFormat format); void BindImageBuffer(Buffer& buffer, u32 offset, u32 size, VideoCore::Surface::PixelFormat format); u64 GetDeviceMemoryUsage() const; void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) { const GLuint handle = fast_uniforms[stage][binding_index].handle; const GLsizeiptr gl_size = static_cast(size); if (use_assembly_shaders) { glBindBufferRangeNV(PABO_LUT[stage], binding_index, handle, 0, gl_size); } else { const GLuint base_binding = graphics_base_uniform_bindings[stage]; const GLuint binding = base_binding + binding_index; glBindBufferRange(GL_UNIFORM_BUFFER, binding, handle, 0, gl_size); } } void PushFastUniformBuffer(size_t stage, u32 binding_index, std::span data) { if (use_assembly_shaders) { glProgramBufferParametersIuivNV( PABO_LUT[stage], binding_index, 0, static_cast(data.size_bytes() / sizeof(GLuint)), reinterpret_cast(data.data())); } else { glNamedBufferSubData(fast_uniforms[stage][binding_index].handle, 0, static_cast(data.size_bytes()), data.data()); } } std::span BindMappedUniformBuffer(size_t stage, u32 binding_index, u32 size) noexcept { const auto [mapped_span, offset] = stream_buffer->Request(static_cast(size)); const GLuint base_binding = graphics_base_uniform_bindings[stage]; const GLuint binding = base_binding + binding_index; glBindBufferRange(GL_UNIFORM_BUFFER, binding, stream_buffer->Handle(), static_cast(offset), static_cast(size)); return mapped_span; } [[nodiscard]] const GLvoid* IndexOffset() const noexcept { return reinterpret_cast(static_cast(index_buffer_offset)); } [[nodiscard]] bool HasFastBufferSubData() const noexcept { return has_fast_buffer_sub_data; } [[nodiscard]] bool SupportsNonZeroUniformOffset() const noexcept { return !use_assembly_shaders; } void SetBaseUniformBindings(const std::array& bindings) { graphics_base_uniform_bindings = bindings; } void SetBaseStorageBindings(const std::array& bindings) { graphics_base_storage_bindings = bindings; } void SetImagePointers(GLuint* texture_handles_, GLuint* image_handles_) { texture_handles = texture_handles_; image_handles = image_handles_; } void SetEnableStorageBuffers(bool use_storage_buffers_) { use_storage_buffers = use_storage_buffers_; } u64 GetDeviceLocalMemory() const { return device_access_memory; } bool CanReportMemoryUsage() const { return device.CanReportMemoryUsage(); } private: static constexpr std::array PABO_LUT{ GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV, GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV, GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV, }; const Device& device; bool has_fast_buffer_sub_data = false; bool use_assembly_shaders = false; bool has_unified_vertex_buffers = false; bool use_storage_buffers = false; u32 max_attributes = 0; std::array graphics_base_uniform_bindings{}; std::array graphics_base_storage_bindings{}; GLuint* texture_handles = nullptr; GLuint* image_handles = nullptr; std::optional stream_buffer; std::array, VideoCommon::NUM_STAGES> fast_uniforms; std::array, VideoCommon::NUM_STAGES> copy_uniforms; std::array copy_compute_uniforms; u32 index_buffer_offset = 0; u64 device_access_memory; }; struct BufferCacheParams { using Runtime = OpenGL::BufferCacheRuntime; using Buffer = OpenGL::Buffer; static constexpr bool IS_OPENGL = true; static constexpr bool HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS = true; static constexpr bool HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT = true; static constexpr bool NEEDS_BIND_UNIFORM_INDEX = true; static constexpr bool NEEDS_BIND_STORAGE_INDEX = true; static constexpr bool USE_MEMORY_MAPS = false; static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true; }; using BufferCache = VideoCommon::BufferCache; } // namespace OpenGL