gpu_texture.h (8935B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #pragma once 5 6 #include "common/gsvector.h" 7 #include "common/types.h" 8 9 #include <algorithm> 10 #include <array> 11 #include <string_view> 12 #include <vector> 13 14 class GPUTexture 15 { 16 public: 17 enum : u32 18 { 19 MAX_WIDTH = 65535, 20 MAX_HEIGHT = 65535, 21 MAX_LAYERS = 255, 22 MAX_LEVELS = 255, 23 MAX_SAMPLES = 255, 24 }; 25 26 enum class Type : u8 27 { 28 Unknown, 29 RenderTarget, 30 DepthStencil, 31 Texture, 32 DynamicTexture, 33 RWTexture, 34 }; 35 36 enum class Format : u8 37 { 38 Unknown, 39 RGBA8, 40 BGRA8, 41 RGB565, 42 RGBA5551, 43 R8, 44 D16, 45 D24S8, 46 D32F, 47 D32FS8, 48 R16, 49 R16I, 50 R16U, 51 R16F, 52 R32I, 53 R32U, 54 R32F, 55 RG8, 56 RG16, 57 RG16F, 58 RG32F, 59 RGBA16, 60 RGBA16F, 61 RGBA32F, 62 RGB10A2, 63 MaxCount 64 }; 65 66 enum class State : u8 67 { 68 Dirty, 69 Cleared, 70 Invalidated 71 }; 72 73 union ClearValue 74 { 75 u32 color; 76 float depth; 77 }; 78 79 public: 80 GPUTexture(const GPUTexture&) = delete; 81 virtual ~GPUTexture(); 82 83 static const char* GetFormatName(Format format); 84 static u32 GetPixelSize(GPUTexture::Format format); 85 static bool IsDepthFormat(GPUTexture::Format format); 86 static bool IsDepthStencilFormat(GPUTexture::Format format); 87 static bool IsCompressedFormat(Format format); 88 static u32 GetCompressedBytesPerBlock(Format format); 89 static u32 GetCompressedBlockSize(Format format); 90 static u32 CalcUploadPitch(Format format, u32 width); 91 static u32 CalcUploadRowLengthFromPitch(Format format, u32 pitch); 92 static u32 CalcUploadSize(Format format, u32 height, u32 pitch); 93 94 static bool ValidateConfig(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format); 95 96 static bool ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u32>& texture_data, u32& texture_data_stride, 97 GPUTexture::Format format); 98 static void FlipTextureDataRGBA8(u32 width, u32 height, u8* texture_data, u32 texture_data_stride); 99 100 ALWAYS_INLINE u32 GetWidth() const { return m_width; } 101 ALWAYS_INLINE u32 GetHeight() const { return m_height; } 102 ALWAYS_INLINE u32 GetLayers() const { return m_layers; } 103 ALWAYS_INLINE u32 GetLevels() const { return m_levels; } 104 ALWAYS_INLINE u32 GetSamples() const { return m_samples; } 105 ALWAYS_INLINE Type GetType() const { return m_type; } 106 ALWAYS_INLINE Format GetFormat() const { return m_format; } 107 ALWAYS_INLINE GSVector4i GetRect() const 108 { 109 return GSVector4i(0, 0, static_cast<s32>(m_width), static_cast<s32>(m_height)); 110 } 111 112 ALWAYS_INLINE bool IsTextureArray() const { return m_layers > 1; } 113 ALWAYS_INLINE bool IsMultisampled() const { return m_samples > 1; } 114 115 ALWAYS_INLINE u32 GetPixelSize() const { return GetPixelSize(m_format); } 116 ALWAYS_INLINE u32 GetMipWidth(u32 level) const { return std::max<u32>(m_width >> level, 1u); } 117 ALWAYS_INLINE u32 GetMipHeight(u32 level) const { return std::max<u32>(m_height >> level, 1u); } 118 119 ALWAYS_INLINE State GetState() const { return m_state; } 120 ALWAYS_INLINE void SetState(State state) { m_state = state; } 121 ALWAYS_INLINE bool IsDirty() const { return (m_state == State::Dirty); } 122 ALWAYS_INLINE bool IsClearedOrInvalidated() const { return (m_state != State::Dirty); } 123 124 ALWAYS_INLINE bool IsRenderTargetOrDepthStencil() const 125 { 126 return (m_type >= Type::RenderTarget && m_type <= Type::DepthStencil); 127 } 128 ALWAYS_INLINE bool IsRenderTarget() const { return (m_type == Type::RenderTarget); } 129 ALWAYS_INLINE bool IsDepthStencil() const { return (m_type == Type::DepthStencil); } 130 ALWAYS_INLINE bool IsTexture() const { return (m_type == Type::Texture || m_type == Type::DynamicTexture); } 131 ALWAYS_INLINE bool IsDynamicTexture() const { return (m_type == Type::DynamicTexture); } 132 ALWAYS_INLINE bool IsRWTexture() const { return (m_type == Type::RWTexture); } 133 134 ALWAYS_INLINE const ClearValue& GetClearValue() const { return m_clear_value; } 135 ALWAYS_INLINE u32 GetClearColor() const { return m_clear_value.color; } 136 ALWAYS_INLINE float GetClearDepth() const { return m_clear_value.depth; } 137 std::array<float, 4> GetUNormClearColor() const; 138 139 ALWAYS_INLINE void SetClearColor(u32 color) 140 { 141 m_state = State::Cleared; 142 m_clear_value.color = color; 143 } 144 ALWAYS_INLINE void SetClearDepth(float depth) 145 { 146 m_state = State::Cleared; 147 m_clear_value.depth = depth; 148 } 149 150 size_t GetVRAMUsage() const; 151 152 u32 GetCompressedBytesPerBlock() const; 153 u32 GetCompressedBlockSize() const; 154 u32 CalcUploadPitch(u32 width) const; 155 u32 CalcUploadRowLengthFromPitch(u32 pitch) const; 156 u32 CalcUploadSize(u32 height, u32 pitch) const; 157 158 GPUTexture& operator=(const GPUTexture&) = delete; 159 160 virtual bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, 161 u32 level = 0) = 0; 162 virtual bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) = 0; 163 virtual void Unmap() = 0; 164 165 // Instructs the backend that we're finished rendering to this texture. It may transition it to a new layout. 166 virtual void MakeReadyForSampling(); 167 168 virtual void SetDebugName(std::string_view name) = 0; 169 170 protected: 171 GPUTexture(u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type, Format format); 172 173 u16 m_width = 0; 174 u16 m_height = 0; 175 u8 m_layers = 0; 176 u8 m_levels = 0; 177 u8 m_samples = 0; 178 Type m_type = Type::Unknown; 179 Format m_format = Format::Unknown; 180 181 State m_state = State::Dirty; 182 183 ClearValue m_clear_value = {}; 184 }; 185 186 class GPUDownloadTexture 187 { 188 public: 189 GPUDownloadTexture(u32 width, u32 height, GPUTexture::Format format, bool is_imported); 190 virtual ~GPUDownloadTexture(); 191 192 /// Basically, this has dimensions only because of DX11. 193 ALWAYS_INLINE u32 GetWidth() const { return m_width; } 194 ALWAYS_INLINE u32 GetHeight() const { return m_height; } 195 ALWAYS_INLINE GPUTexture::Format GetFormat() const { return m_format; } 196 ALWAYS_INLINE bool NeedsFlush() const { return m_needs_flush; } 197 ALWAYS_INLINE bool IsMapped() const { return (m_map_pointer != nullptr); } 198 ALWAYS_INLINE bool IsImported() const { return m_is_imported; } 199 ALWAYS_INLINE const u8* GetMapPointer() const { return m_map_pointer; } 200 ALWAYS_INLINE u32 GetMapPitch() const { return m_current_pitch; } 201 202 /// Calculates the pitch of a transfer. 203 u32 GetTransferPitch(u32 width, u32 pitch_align) const; 204 205 /// Calculates the size of the data you should transfer. 206 void GetTransferSize(u32 x, u32 y, u32 width, u32 height, u32 pitch, u32* copy_offset, u32* copy_size, 207 u32* copy_rows) const; 208 209 /// Queues a copy from the specified texture to this buffer. 210 /// Does not complete immediately, you should flush before accessing the buffer. 211 /// use_transfer_pitch should be true if there's only a single texture being copied to this buffer before 212 /// it will be used. This allows the image to be packed tighter together, and buffer reuse. 213 virtual void CopyFromTexture(u32 dst_x, u32 dst_y, GPUTexture* src, u32 src_x, u32 src_y, u32 width, u32 height, 214 u32 src_layer, u32 src_level, bool use_transfer_pitch = true) = 0; 215 216 /// Maps the texture into the CPU address space, enabling it to read the contents. 217 /// The Map call may not perform synchronization. If the contents of the staging texture 218 /// has been updated by a CopyFromTexture() call, you must call Flush() first. 219 /// If persistent mapping is supported in the backend, this may be a no-op. 220 virtual bool Map(u32 x, u32 y, u32 width, u32 height) = 0; 221 222 /// Unmaps the CPU-readable copy of the texture. May be a no-op on backends which 223 /// support persistent-mapped buffers. 224 virtual void Unmap() = 0; 225 226 /// Flushes pending writes from the CPU to the GPU, and reads from the GPU to the CPU. 227 /// This may cause a command buffer submit depending on if one has occurred between the last 228 /// call to CopyFromTexture() and the Flush() call. 229 virtual void Flush() = 0; 230 231 /// Sets object name that will be displayed in graphics debuggers. 232 virtual void SetDebugName(std::string_view name) = 0; 233 234 /// Reads the specified rectangle from the staging texture to out_ptr, with the specified stride 235 /// (length in bytes of each row). CopyFromTexture() must be called first. The contents of any 236 /// texels outside of the rectangle used for CopyFromTexture is undefined. 237 bool ReadTexels(u32 x, u32 y, u32 width, u32 height, void* out_ptr, u32 out_stride); 238 239 /// Returns what the size of the specified texture would be, in bytes. 240 static u32 GetBufferSize(u32 width, u32 height, GPUTexture::Format format, u32 pitch_align = 1); 241 242 protected: 243 u32 m_width; 244 u32 m_height; 245 GPUTexture::Format m_format; 246 247 const u8* m_map_pointer = nullptr; 248 u32 m_current_pitch = 0; 249 250 bool m_is_imported = false; 251 bool m_needs_flush = false; 252 };