910e62b5创建于 1月15日历史提交
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef GPU_COMMAND_BUFFER_SERVICE_BUFFER_MANAGER_H_
#define GPU_COMMAND_BUFFER_SERVICE_BUFFER_MANAGER_H_

#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>

#include <map>
#include <memory>
#include <unordered_map>
#include <vector>

#include "base/check_op.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/trace_event/memory_dump_provider.h"
#include "gpu/command_buffer/common/buffer.h"
#include "gpu/command_buffer/service/gl_utils.h"
#include "gpu/command_buffer/service/memory_tracking.h"
#include "gpu/gpu_gles2_export.h"

namespace gpu {
namespace gles2 {

class BufferManager;
struct ContextState;
class ErrorState;
class FeatureInfo;
class IndexedBufferBindingHost;
class TestHelper;

// Info about Buffers currently in the system.
class GPU_GLES2_EXPORT Buffer : public base::RefCounted<Buffer> {
 public:
  struct MappedRange {
    GLintptr offset;
    GLsizeiptr size;
    GLenum access;
    raw_ptr<void, DanglingUntriaged> pointer;  // Pointer returned by driver.
    scoped_refptr<gpu::Buffer> shm;  // Client side mem buffer.
    unsigned int shm_offset;  // Client side mem buffer offset.

    MappedRange(GLintptr offset, GLsizeiptr size, GLenum access, void* pointer,
                scoped_refptr<gpu::Buffer> shm, unsigned int shm_offset);
    ~MappedRange();
    void* GetShmPointer() const;
  };

  Buffer(BufferManager* manager, GLuint service_id);

  GLenum initial_target() const { return initial_target_; }

  GLuint service_id() const {
    return service_id_;
  }

  GLsizeiptr size() const {
    return size_;
  }

  GLenum usage() const {
    return usage_;
  }

  bool shadowed() const {
    return !shadow_.empty();
  }

  // Gets the maximum value in the buffer for the given range interpreted as
  // the given type. Returns false if offset and count are out of range.
  // offset is in bytes.
  // count is in elements of type.
  bool GetMaxValueForRange(GLuint offset, GLsizei count, GLenum type,
                           bool primitive_restart_enabled, GLuint* max_value);

  // Returns a pointer to shadowed data.
  const void* GetRange(GLintptr offset, GLsizeiptr size) const;

  // Check if an offset, size range is valid for the current buffer.
  bool CheckRange(GLintptr offset, GLsizeiptr size) const;

  // Sets a range of this buffer's shadowed data.
  void SetRange(GLintptr offset, GLsizeiptr size, const GLvoid * data);

  bool IsDeleted() const {
    return deleted_;
  }

  bool IsValid() const {
    return initial_target() && !IsDeleted();
  }

  bool IsClientSideArray() const {
    return is_client_side_array_;
  }

  void SetMappedRange(GLintptr offset, GLsizeiptr size, GLenum access,
                      void* pointer, scoped_refptr<gpu::Buffer> shm,
                      unsigned int shm_offset);
  void RemoveMappedRange();
  const MappedRange* GetMappedRange() const {
    return mapped_range_.get();
  }

  // These maintain the reference counts for checking whether a buffer is
  // double-bound to transform feedback and non-transform-feedback binding
  // points.
  void OnBind(GLenum target, bool indexed);
  void OnUnbind(GLenum target, bool indexed);

  bool IsBoundForTransformFeedback() const {
    return transform_feedback_indexed_binding_count_ > 0;
  }

  bool IsBoundForTransformFeedbackAndOther() const {
    return transform_feedback_indexed_binding_count_ > 0 &&
           non_transform_feedback_binding_count_ > 0;
  }

  bool IsDoubleBoundForTransformFeedback() const {
    return transform_feedback_indexed_binding_count_ > 1;
  }

  void SetReadbackShadowAllocation(scoped_refptr<gpu::Buffer> shm,
                                   uint32_t shm_offset);
  scoped_refptr<gpu::Buffer> TakeReadbackShadowAllocation(void** data);

 private:
  friend class BufferManager;
  friend class BufferManagerTestBase;
  friend class base::RefCounted<Buffer>;

  // Represents a range in a buffer.
  class Range {
   public:
    Range(GLuint offset, GLsizei count, GLenum type,
          bool primitive_restart_enabled)
        : offset_(offset),
          count_(count),
          type_(type),
          primitive_restart_enabled_(primitive_restart_enabled) {
    }

    // A less functor provided for std::map so it can find ranges.
    struct Less {
      bool operator() (const Range& lhs, const Range& rhs) const {
        if (lhs.offset_ != rhs.offset_) {
          return lhs.offset_ < rhs.offset_;
        }
        if (lhs.count_ != rhs.count_) {
          return lhs.count_ < rhs.count_;
        }
        if (lhs.type_ != rhs.type_) {
          return lhs.type_ < rhs.type_;
        }
        return lhs.primitive_restart_enabled_ < rhs.primitive_restart_enabled_;
      }
    };

   private:
    GLuint offset_;
    GLsizei count_;
    GLenum type_;
    bool primitive_restart_enabled_;
  };

  ~Buffer();

  void set_initial_target(GLenum target) {
    DCHECK_EQ(0u, initial_target_);
    initial_target_ = target;
  }

  void MarkAsDeleted() {
    deleted_ = true;
  }

  // Setup the shadow buffer. This will either initialize the shadow buffer
  // with the passed data or clear the shadow buffer if no shadow required. This
  // will return a pointer to the shadowed data if using shadow, otherwise will
  // return the original data pointer.
  const GLvoid* StageShadow(bool use_shadow,
                            GLsizeiptr size,
                            const GLvoid* data);

  // Sets the size, usage and initial data of a buffer.
  // If shadow is true then if data is NULL buffer will be initialized to 0.
  void SetInfo(GLsizeiptr size,
               GLenum usage,
               bool use_shadow,
               bool is_client_side_array);

  // Clears any cache of index ranges.
  void ClearCache();

  // The manager that owns this Buffer.
  raw_ptr<BufferManager> manager_;

  // A copy of the data in the buffer. This data is only kept if the conditions
  // checked in UseShadowBuffer() are true.
  std::vector<uint8_t> shadow_;

  // Size of buffer.
  GLsizeiptr size_;

  // True if deleted.
  bool deleted_;

  // Whether or not this Buffer is not uploaded to the GPU but just
  // sitting in local memory.
  bool is_client_side_array_;

  // Keeps track of whether this buffer is currently bound for transform
  // feedback in a WebGL context. Used as an optimization when validating WebGL
  // draw calls for compliance with binding restrictions.
  // http://crbug.com/696345
  int non_transform_feedback_binding_count_ = 0;
  int transform_feedback_indexed_binding_count_ = 0;

  // Service side buffer id.
  GLuint service_id_;

  // The first target of buffer. 0 = unset.
  // It is set the first time bindBuffer() is called and cannot be changed.
  GLenum initial_target_;

  // Usage of buffer.
  GLenum usage_;

  // Data cached from last glMapBufferRange call.
  std::unique_ptr<MappedRange> mapped_range_;

  // A map of ranges to the highest value in that range of a certain type.
  typedef std::map<Range, GLuint, Range::Less> RangeToMaxValueMap;
  RangeToMaxValueMap range_set_;

  scoped_refptr<gpu::Buffer> readback_shm_;
  uint32_t readback_shm_offset_ = 0;
};

// This class keeps track of the buffers and their sizes so we can do
// bounds checking.
//
// NOTE: To support shared resources an instance of this class will need to be
// shared by multiple GLES2Decoders.
class GPU_GLES2_EXPORT BufferManager
    : public base::trace_event::MemoryDumpProvider {
 public:
  BufferManager(scoped_refptr<MemoryTracker> memory_tracker,
                FeatureInfo* feature_info);

  BufferManager(const BufferManager&) = delete;
  BufferManager& operator=(const BufferManager&) = delete;

  ~BufferManager() override;

  void MarkContextLost();

  // Must call before destruction.
  void Destroy();

  // Creates a Buffer for the given buffer.
  void CreateBuffer(GLuint client_id, GLuint service_id);

  // Gets the buffer info for the given buffer.
  Buffer* GetBuffer(GLuint client_id);

  // Removes a buffer info for the given buffer.
  void RemoveBuffer(GLuint client_id);

  // Gets a client id for a given service id.
  bool GetClientId(GLuint service_id, GLuint* client_id) const;

  // Validates a glBufferSubData, and then calls DoBufferData if validation was
  // successful.
  void ValidateAndDoBufferSubData(ContextState* context_state,
                                  ErrorState* error_state,
                                  GLenum target,
                                  GLintptr offset,
                                  GLsizeiptr size,
                                  const GLvoid* data);

  // Validates a glBufferData, and then calls DoBufferData if validation was
  // successful.
  void ValidateAndDoBufferData(ContextState* context_state,
                               ErrorState* error_state,
                               GLenum target,
                               GLsizeiptr size,
                               const GLvoid* data,
                               GLenum usage);

  // Validates a glCopyBufferSubData, and then calls DoCopyBufferSubData if
  // validation was successful.
  void ValidateAndDoCopyBufferSubData(ContextState* context_state,
                                      ErrorState* error_state,
                                      GLenum readtarget,
                                      GLenum writetarget,
                                      GLintptr readoffset,
                                      GLintptr writeoffset,
                                      GLsizeiptr size);

  // Validates a glGetBufferParameteri64v, and then calls GetBufferParameteri64v
  // if validation was successful.
  void ValidateAndDoGetBufferParameteri64v(ContextState* context_state,
                                           ErrorState* error_state,
                                           GLenum target,
                                           GLenum pname,
                                           GLint64* params);

  // Validates a glGetBufferParameteriv, and then calls GetBufferParameteriv if
  // validation was successful.
  void ValidateAndDoGetBufferParameteriv(ContextState* context_state,
                                         ErrorState* error_state,
                                         GLenum target,
                                         GLenum pname,
                                         GLint* params);

  // Sets the target of a buffer. Returns false if the target can not be set.
  bool SetTarget(Buffer* buffer, GLenum target);

  void set_max_buffer_size(GLsizeiptr max_buffer_size) {
    max_buffer_size_ = max_buffer_size;
  }

  void set_allow_buffers_on_multiple_targets(bool allow) {
    allow_buffers_on_multiple_targets_ = allow;
  }

  void set_allow_fixed_attribs(bool allow) {
    allow_fixed_attribs_ = allow;
  }

  size_t mem_represented() const {
    return memory_type_tracker_->GetMemRepresented();
  }

  // Tells for a given usage if this would be a client side array.
  bool IsUsageClientSideArray(GLenum usage);

  // Tells whether a buffer that is emulated using client-side arrays should be
  // set to a non-zero size.
  bool UseNonZeroSizeForClientSideArrayBuffer();

  void SetPrimitiveRestartFixedIndexIfNecessary(GLenum type);

  Buffer* GetBufferInfoForTarget(ContextState* state, GLenum target) const;

  // base::trace_event::MemoryDumpProvider implementation.
  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
                    base::trace_event::ProcessMemoryDump* pmd) override;

  // Validate if a buffer is bound at target, if it's unmapped, if it's
  // large enough. Return the buffer bound to |target| if access is granted;
  // return nullptr if a GL error is generated.
  // Generates INVALID_VALUE if offset + size is out of range.
  Buffer* RequestBufferAccess(ContextState* context_state,
                              ErrorState* error_state,
                              GLenum target,
                              GLintptr offset,
                              GLsizeiptr size,
                              const char* func_name);
  // Same as above, but assume to access the entire buffer.
  Buffer* RequestBufferAccess(ContextState* context_state,
                              ErrorState* error_state,
                              GLenum target,
                              const char* func_name);
  // Same as above, but it can be any buffer rather than the buffer bound to
  // |target|. Return true if access is granted; return false if a GL error is
  // generated.
  bool RequestBufferAccess(ErrorState* error_state,
                           Buffer* buffer,
                           const char* func_name,
                           const char* error_message_format,
                           ...);
  // Generates INVALID_OPERATION if offset + size is out of range.
  bool RequestBufferAccess(ErrorState* error_state,
                           Buffer* buffer,
                           GLintptr offset,
                           GLsizeiptr size,
                           const char* func_name,
                           const char* error_message);
  // Returns false and generates INVALID_OPERATION if buffer at binding |ii|
  // doesn't exist, is mapped, or smaller than |variable_sizes[ii]| * |count|.
  // Return true otherwise.
  bool RequestBuffersAccess(ErrorState* error_state,
                            const IndexedBufferBindingHost* bindings,
                            const std::vector<GLsizeiptr>& variable_sizes,
                            GLsizei count,
                            const char* func_name,
                            const char* message_tag);

 private:
  friend class Buffer;
  friend class TestHelper;  // Needs access to DoBufferData.
  friend class BufferManagerTestBase;  // Needs access to DoBufferSubData.
  friend class IndexedBufferBindingHostTest;  // Needs access to SetInfo.

  void StartTracking(Buffer* buffer);
  void StopTracking(Buffer* buffer);

  // Does a glBufferSubData and updates the appropriate accounting.
  // Assumes the values have already been validated.
  void DoBufferSubData(
      Buffer* buffer,
      GLenum target,
      GLintptr offset,
      GLsizeiptr size,
      const GLvoid* data);

  // Does a glBufferData and updates the appropriate accounting.
  // Assumes the values have already been validated.
  void DoBufferData(
      ErrorState* error_state,
      Buffer* buffer,
      GLenum target,
      GLsizeiptr size,
      GLenum usage,
      const GLvoid* data);

  // Does a glCopyBufferSubData and updates the appropriate accounting.
  // Assumes the values have already been validated.
  void DoCopyBufferSubData(
      Buffer* readbuffer,
      GLenum readtarget,
      GLintptr readoffset,
      Buffer* writebuffer,
      GLenum writetarget,
      GLintptr writeoffset,
      GLsizeiptr size);

  // Tests whether a shadow buffer needs to be used.
  bool UseShadowBuffer(GLenum target, GLenum usage);

  // Sets the size, usage and initial data of a buffer.
  // If data is NULL buffer will be initialized to 0 if shadowed.
  void SetInfo(Buffer* buffer,
               GLenum target,
               GLsizeiptr size,
               GLenum usage,
               bool use_shadow);

  // Same as public RequestBufferAccess taking similar arguments, but
  // allows caller to assemble the va_list.
  bool RequestBufferAccessV(ErrorState* error_state,
                            Buffer* buffer,
                            const char* func_name,
                            const char* error_message_format,
                            va_list varargs);

  std::unique_ptr<MemoryTypeTracker> memory_type_tracker_;
  scoped_refptr<FeatureInfo> feature_info_;

  // Info for each buffer in the system.
  typedef std::unordered_map<GLuint, scoped_refptr<Buffer>> BufferMap;
  BufferMap buffers_;

  // The maximum size of buffers.
  GLsizeiptr max_buffer_size_;

  // Whether or not buffers can be bound to multiple targets.
  bool allow_buffers_on_multiple_targets_;

  // Whether or not allow using GL_FIXED type for vertex attribs.
  bool allow_fixed_attribs_;

  // Counts the number of Buffer allocated with 'this' as its manager.
  // Allows to check no Buffer will outlive this.
  unsigned int buffer_count_;

  GLuint primitive_restart_fixed_index_;

  bool lost_context_;
  bool use_client_side_arrays_for_stream_buffers_;
};

}  // namespace gles2
}  // namespace gpu

#endif  // GPU_COMMAND_BUFFER_SERVICE_BUFFER_MANAGER_H_