Overte C++ Documentation
VKTexture.h
1 //
2 // Created by Bradley Austin Davis on 2016/08/07
3 // Adapted for Vulkan in 2022-2025 by dr Karol Suprynowicz.
4 // Copyright 2013-2018 High Fidelity, Inc.
5 // Copyright 2023-2025 Overte e.V.
6 //
7 // Distributed under the Apache License, Version 2.0.
8 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
9 // SPDX-License-Identifier: Apache-2.0
10 //
11 // Contains parts of Vulkan Samples, Copyright (c) 2018, Sascha Willems, distributed on MIT License.
12 //
13 
14 #ifndef hifi_gpu_vk_VKTexture_h
15 #define hifi_gpu_vk_VKTexture_h
16 
17 #include "VKShared.h"
18 
19 #include "VKBackend.h"
20 
21 
22 namespace gpu { namespace vk {
23 
24 struct VKFilterMode {
25  VkFilter minFilter;
26  VkFilter magFilter;
27 };
28 
29 class VKTexture : public VKObject<Texture> {
30 public:
31  // VKTODO
32  //static void initTextureTransferHelper();
33  //static std::shared_ptr<VKTextureTransferHelper> _textureTransferHelper;
34 
35  /*template <typename VKTextureType>
36  static VKTextureType* sync(VKBackend& backend, const TexturePointer& texturePointer, bool needTransfer) {
37  const Texture& texture = *texturePointer;
38  if (!texture.isDefined()) {
39  // NO texture definition yet so let's avoid thinking
40  return nullptr;
41  }
42 
43  // If the object hasn't been created, or the object definition is out of date, drop and re-create
44  VKTextureType* object = Backend::getGPUObject<VKTextureType>(texture);
45 
46  // Create the texture if need be (force re-creation if the storage stamp changes
47  // for easier use of immutable storage)
48  if (!object || object->isInvalid()) {
49  // This automatically any previous texture
50  object = new VKTextureType(backend.shared_from_this(), texture, needTransfer);
51  if (!object->_transferable) {
52  object->createTexture(backend);
53  object->_contentStamp = texture.getDataStamp();
54  object->postTransfer(backend);
55  }
56  }
57 
58  // Object maybe doesn't need to be transferred after creation
59  if (!object->_transferable) {
60  return object;
61  }
62 
63  // If we just did a transfer, return the object after doing post-transfer work
64  if (VKSyncState::Transferred == object->getSyncState()) {
65  object->postTransfer(backend);
66  return object;
67  }
68 
69  if (object->isReady()) {
70  // Do we need to reduce texture memory usage?
71  if (object->isOverMaxMemory() && texturePointer->incremementMinMip()) {
72  // WARNING, this code path will essentially `delete this`,
73  // so no dereferencing of this instance should be done past this point
74  object = new VKTextureType(backend.shared_from_this(), texture, object);
75  // VKTODO
76  // _textureTransferHelper->transferTexture(texturePointer);
77  }
78  } else if (object->isOutdated()) {
79  // Object might be outdated, if so, start the transfer
80  // (outdated objects that are already in transfer will have reported 'true' for ready()
81  // VKTODO
82  // _textureTransferHelper->transferTexture(texturePointer);
83  }
84 
85  return object;
86  }*/
87 
88  /*template <typename VKTextureType>
89  static VkDescriptorImageInfo getDescriptorImageInfoFromTexture(VKBackend& backend, const TexturePointer& texture, bool shouldSync) {
90  if (!texture) {
91  Q_ASSERT(false);
92  return backend.getDefaultTextureDescriptorInfo();
93  }
94  VKTextureType* object { nullptr };
95  if (shouldSync) {
96  object = sync<VKTextureType>(backend, texture, shouldSync);
97  } else {
98  object = Backend::getGPUObject<VKTextureType>(*texture);
99  }
100  if (!object) {
101  Q_ASSERT(false);
102  return backend.getDefaultTextureDescriptorInfo();
103  }
104 
105  VkDescriptorImageInfo result {};
106  //= object->_;
107 
108  // VKTODO
109  // Don't return textures that are in transfer state
110  //if (shouldSync) {
111  // if ((object->getSyncState() != VKSyncState::Idle) ||
112  // // Don't return transferable textures that have never completed transfer
113  // (!object->_transferable || 0 != object->_transferCount)) {
114  // // Will be either 0 or the original texture being downsampled.
115  // Q_ASSERT(false);
116  // result = object->_downsampleSource._texture;
117  // }
118  //}
119 
120  return object->getDescriptorImageInfo();
121  }*/
122 
123  // Used by derived classes and helpers to ensure the actual VK object exceeds the lifetime of `this`
124  /*VkImage takeOwnership() {
125  VkImage result = _id;
126  const_cast<VkImage&>(_id) = 0;
127  return result;
128  }*/
129 
130  virtual ~VKTexture();
131 
132  VkImage _vkImage{ VK_NULL_HANDLE };
133  const Stamp _storageStamp;
134  const VkImageViewType _target;
135  //const uint16 _maxMip;
136  //const uint16 _minMip;
137  //const Size _virtualSize; // theoretical size as expected
138  Stamp _contentStamp { 0 };
139  const bool _transferable;
140  Size _transferCount { 0 };
141 
142  struct DownsampleSource {
143  using Pointer = std::shared_ptr<DownsampleSource>;
144  DownsampleSource(const std::weak_ptr<VKBackend>& backend) : _backend(backend), _size(0), _texture(VK_NULL_HANDLE), _minMip(0), _maxMip(0) {}
145  DownsampleSource(const std::weak_ptr<VKBackend>& backend, VKTexture* originalTexture) : _backend(backend), _size(0), _texture(0), _minMip(0), _maxMip(0) { Q_ASSERT(false); }; // VKTODO
146  ~DownsampleSource() {}; // VKTODO
147  void reset() { _texture = VK_NULL_HANDLE; }
148  const std::weak_ptr<vk::VKBackend>& _backend;
149  const Size _size { 0 };
150  // TODO: Shouldn't that be texture instead?
151  VkImage _texture { VK_NULL_HANDLE };
152  const uint16 _minMip { 0 };
153  const uint16 _maxMip { 0 };
154  } _downsampleSource;
155 
156  virtual size_t size() const { return _size; }
157  VKSyncState getSyncState() const { return _syncState; }
158 
159  // Is the storage out of date relative to the gpu texture?
160  // VKTODO
161  bool isInvalid() const { return false; };
162 
163  // Is the content out of date relative to the gpu texture?
164  // VKTODO
165  bool isOutdated() const { return false; };
166 
167  // Is the texture in a state where it can be rendered with no work?
168  bool isReady() const {
169  // VKTODO: Is this right?
170  return _syncState == VKSyncState::Transferred;
171  };
172 
173  // Execute any post-move operations that must occur only on the main thread
174  virtual void postTransfer(VKBackend &backend) = 0;
175 
176  // VKTODO: can be done later
177  bool isOverMaxMemory() const { return false; };
178 
179 protected:
180  static const size_t CUBE_NUM_FACES = 6;
181  // VKTODO
182  //static const uint8_t TEXTURE_CUBE_NUM_FACES = 6;
183  //static const uint32_t CUBE_FACE_LAYOUT[TEXTURE_CUBE_NUM_FACES];
184  static const VKFilterMode FILTER_MODES[Sampler::NUM_FILTERS];
185  static const uint32_t WRAP_MODES[Sampler::NUM_WRAP_MODES];
186 
187  //static const std::vector<VKenum>& getFaceTargets(VKenum textureType);
188 
189  static VkImageViewType getVKTextureType(const Texture& texture);
190  // Return a floating point value indicating how much of the allowed
191  // texture memory we are currently consuming. A value of 0 indicates
192  // no texture memory usage, while a value of 1 indicates all available / allowed memory
193  // is consumed. A value above 1 indicates that there is a problem.
194  // VKTODO: this can wait for now
195  static float getMemoryPressure() { return 0; };
196 
197 
198  const size_t _size { 0 }; // true size as reported by the Vulkan api
199  std::atomic<VKSyncState> _syncState { VKSyncState::Idle };
200  VkFormat _vkTexelFormat { VK_FORMAT_UNDEFINED };
201 
202  VKTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture, bool isTransferable);
203  //VKTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture, VkImage image, bool transferrable);
204  //VKTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture, VkImage image, VKTexture* originalTexture);
205 
206  void setSyncState(VKSyncState syncState) { _syncState = syncState; }
207  //uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; }
208 
209  virtual void createTexture(VKBackend &backend) = 0;
210  virtual VkDescriptorImageInfo getDescriptorImageInfo() = 0;
211 
212  //virtual void allocateStorage() const = 0;
213  //virtual void updateSize() const = 0;
214  struct TransferData {
215  uint16_t mipLevels;
216  uint16_t width;
217  uint16_t height;
218  size_t buffer_size;
219  struct Mip {
220  size_t offset;
221  size_t size;
222  uint32_t width;
223  uint32_t height;
224  bool needsAddingAlpha;
225  bool needsBGRToRGB;
226  std::shared_ptr<const storage::Storage> data;
227  };
228  std::vector<std::vector<Mip>> mips;
229  };
230  TransferData _transferData{};
231  virtual void transfer(VKBackend &backend) = 0;
232  //virtual void syncSampler() const = 0;
233  // VKTODO
234  //virtual void generateMips() const = 0;
235  // VKTODO what does this mean?
236  //virtual void withPreservedTexture(std::function<void()> f) const = 0;
237 
238 protected:
239  void setSize(size_t size) const;
240 
241 private:
242 
243  VKTexture(const std::weak_ptr<VKBackend>& backend, const gpu::Texture& gpuTexture, VkImage image, VKTexture* originalTexture, bool transferrable);
244 
245  friend class VKTextureTransferHelper;
246  friend class VKBackend;
247 };
248 
249 class VKFixedAllocationTexture : public VKTexture {
250  using Parent = VKTexture;
251  friend class VKBackend;
252 
253 public:
254  VKFixedAllocationTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture, bool isTransferable) :
255  VKTexture(backend, texture, isTransferable) {}; // VKTODO
256  virtual ~VKFixedAllocationTexture() {};
257 
258 protected:
259  Size size() const override { return _size; }
260 
261  //void allocateStorage();
262  // VKTODO
263  //void syncSampler() const override;
264  // VKTODO
265  //void updateSize() const override {};
266  VmaAllocation _vmaAllocation;
267  const Size _size{ 0 };
268 };
269 
270 class VKAttachmentTexture : public VKFixedAllocationTexture {
271  friend class VKBackend;
272  friend class VKFramebuffer;
273 
274 public:
275  VkImageLayout getVkImageLayout() { return _vkImageLayout; };
276 
277 protected:
278  VKAttachmentTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture) :
279  VKFixedAllocationTexture(backend, texture, false) {
280  VKAttachmentTexture::createTexture(*backend.lock());
281  };
282  virtual ~VKAttachmentTexture(); // VKTODO: delete image and image view, release memory
283  void createTexture(VKBackend &backend) override;
284  void transfer(VKBackend &backend) override {}; // VKTODO
285  void postTransfer(VKBackend &backend) override {}; // VKTODO
286 
287  VkDescriptorImageInfo getDescriptorImageInfo() override; // VKTODO
288 
289  VkImageView _vkImageView { VK_NULL_HANDLE }; // Created by VKFramebuffer class.
290  VkImageLayout _vkImageLayout {}; // VKTODO: this needs to be updated on blits and other image writes
291  VkSampler _vkSampler { VK_NULL_HANDLE };
292 };
293 
294 class VKStrictResourceTexture: public VKFixedAllocationTexture {
295  friend class VKBackend;
296 
297 protected:
298  // VKTODO: how to handle mipmaps?
299  VKStrictResourceTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture) :
300  VKFixedAllocationTexture(backend, texture, true) {
301  VKBackend& vkBackend = *backend.lock();
302  VKStrictResourceTexture::createTexture(vkBackend);
303  // VKTODO: transfer on transfer tread
304  VKStrictResourceTexture::transfer(vkBackend);
305  VKStrictResourceTexture::postTransfer(vkBackend);
306  };
307  ~VKStrictResourceTexture() override; // VKTODO: delete image and image view, release memory
308  void createTexture(VKBackend &backend) override;
309  void transfer(VKBackend &backend) override;
310  void postTransfer(VKBackend &backend) override;
311  VkDescriptorImageInfo getDescriptorImageInfo() override;
312  //VkImage _vkImage { VK_NULL_HANDLE };
313  VkImageView _vkImageView { VK_NULL_HANDLE };
314  VkImageLayout _vkImageLayout {};
315  VkSampler _vkSampler { VK_NULL_HANDLE };
316  // This need to be moved to VKFixedAllocationTexture and allocated in allocateStorage()
317  //VkDeviceMemory _vkDeviceMemory{ VK_NULL_HANDLE };
318 };
319 
320 class VKExternalTexture: public VKTexture {
321  friend class VKBackend;
322 
323 public:
324  VKExternalTexture(const std::weak_ptr<VKBackend>& backend, const Texture& texture) :
325  VKTexture(backend, texture, false) {
326  VKBackend& vkBackend = *backend.lock();
327  VKExternalTexture::createTexture(vkBackend);
328  // VKTODO: maybe this needs to be done on OpenGL thread?
329  VKExternalTexture::initGL(vkBackend);
330  //VKExternalTexture::transferGL(vkBackend);
331  VKExternalTexture::postTransfer(vkBackend);
332  }; // VKTODO
333  ~VKExternalTexture() override; // VKTODO: add proper cleanup for both Vulkan and OpenGL
334  void releaseExternalTexture(); // Called on destructor and when new texture is set
335  void setSource(GLuint source);
336  void transferGL(VKBackend &backend);
337 
338 protected:
339  Size size() const override { return _size; }
340 
341  //VmaAllocation _vmaAllocation;
342  VkDeviceMemory _sharedMemory;
343  size_t _sharedMemorySize;
344  const Size _size{ 0 }; // VKTODO: how is this used?
345 
346  void createTexture(VKBackend &backend) override;
347  void initGL(VKBackend &backend);
348  void transfer(VKBackend &backend) override {};
349  void postTransfer(VKBackend &backend) override;
350  VkDescriptorImageInfo getDescriptorImageInfo() override;
351  VkImageView _vkImageView { VK_NULL_HANDLE };
352  VkImageLayout _vkImageLayout {};
353  VkSampler _vkSampler { VK_NULL_HANDLE };
354 
355  // Shared texture properties
356 #ifdef WIN32
357  HANDLE _sharedHandle { nullptr }; // Windows handle for shared texture memory.
358 #else
359  int _sharedFd { -1 }; // File descriptor is used for sharing memory between Vulkan and OpenGL on Linux.
360 #endif
361  GLuint _openGLMemoryObject = 0;
362  GLuint _openGLId = 0;
363  GLuint _openGLSourceId = 0;
364 };
365 
366 } }
367 
368 #endif
A simple object wrapper for an OpenGL texture.
Definition: material-networking/src/material-networking/TextureCache.h:39